home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2010 April / PCA177.iso / ESSENTIALS / Firefox Setup.exe / nonlocalized / components / nsUpdateService.js < prev    next >
Encoding:
Text File  |  2009-07-15  |  104.7 KB  |  3,226 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3. //@line 44 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  4. */
  5.  
  6. const Cc = Components.classes;
  7. const Ci = Components.interfaces;
  8. const Cr = Components.results;
  9.  
  10. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  11.  
  12. const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
  13. const PREF_APP_UPDATE_AUTO                = "app.update.auto";
  14. const PREF_APP_UPDATE_MODE                = "app.update.mode";
  15. const PREF_APP_UPDATE_SILENT              = "app.update.silent";
  16. const PREF_APP_UPDATE_INTERVAL            = "app.update.interval";
  17. const PREF_APP_UPDATE_TIMER               = "app.update.timer";
  18. const PREF_APP_UPDATE_IDLETIME            = "app.update.idletime";
  19. const PREF_APP_UPDATE_PROMPTWAITTIME      = "app.update.promptWaitTime";
  20. const PREF_APP_UPDATE_LOG_BRANCH          = "app.update.log.";
  21. const PREF_APP_UPDATE_URL                 = "app.update.url";
  22. const PREF_APP_UPDATE_URL_OVERRIDE        = "app.update.url.override";
  23. const PREF_APP_UPDATE_URL_DETAILS         = "app.update.url.details";
  24. const PREF_APP_UPDATE_CHANNEL             = "app.update.channel";
  25. const PREF_APP_UPDATE_SHOW_INSTALLED_UI   = "app.update.showInstalledUI";
  26. const PREF_APP_UPDATE_LASTUPDATETIME_FMT  = "app.update.lastUpdateTime.%ID%";
  27. const PREF_APP_UPDATE_INCOMPATIBLE_MODE   = "app.update.incompatible.mode";
  28. const PREF_UPDATE_NEVER_BRANCH            = "app.update.never.";
  29. const PREF_PARTNER_BRANCH                 = "app.partner.";
  30. const PREF_APP_DISTRIBUTION               = "distribution.id";
  31. const PREF_APP_DISTRIBUTION_VERSION       = "distribution.version";
  32.  
  33. const URI_UPDATE_PROMPT_DIALOG  = "chrome://mozapps/content/update/updates.xul";
  34. const URI_UPDATE_HISTORY_DIALOG = "chrome://mozapps/content/update/history.xul";
  35. const URI_BRAND_PROPERTIES      = "chrome://branding/locale/brand.properties";
  36. const URI_UPDATES_PROPERTIES    = "chrome://mozapps/locale/update/updates.properties";
  37. const URI_UPDATE_NS             = "http://www.mozilla.org/2005/app-update";
  38.  
  39. const KEY_APPDIR          = "XCurProcD";
  40. const KEY_GRED            = "GreD";
  41. //@line 82 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  42. const KEY_UPDROOT         = "UpdRootD";
  43. const KEY_UAPPDATA        = "UAppData";
  44. //@line 85 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  45.  
  46. const DIR_UPDATES         = "updates";
  47. const FILE_UPDATE_STATUS  = "update.status";
  48. const FILE_UPDATE_VERSION = "update.version";
  49. const FILE_UPDATE_ARCHIVE = "update.mar";
  50. const FILE_UPDATE_LOG     = "update.log"
  51. const FILE_UPDATES_DB     = "updates.xml";
  52. const FILE_UPDATE_ACTIVE  = "active-update.xml";
  53. const FILE_PERMS_TEST     = "update.test";
  54. const FILE_LAST_LOG       = "last-update.log";
  55. const FILE_UPDATE_LOCALE  = "update.locale";
  56.  
  57. const MODE_RDONLY   = 0x01;
  58. const MODE_WRONLY   = 0x02;
  59. const MODE_CREATE   = 0x08;
  60. const MODE_APPEND   = 0x10;
  61. const MODE_TRUNCATE = 0x20;
  62.  
  63. const PERMS_FILE      = 0644;
  64. const PERMS_DIRECTORY = 0755;
  65.  
  66. const STATE_NONE            = "null";
  67. const STATE_DOWNLOADING     = "downloading";
  68. const STATE_PENDING         = "pending";
  69. const STATE_APPLYING        = "applying";
  70. const STATE_SUCCEEDED       = "succeeded";
  71. const STATE_DOWNLOAD_FAILED = "download-failed";
  72. const STATE_FAILED          = "failed";
  73.  
  74. // From updater/errors.h:
  75. const WRITE_ERROR = 7;
  76.  
  77. const DOWNLOAD_CHUNK_SIZE           = 300000; // bytes
  78. const DOWNLOAD_BACKGROUND_INTERVAL  = 600;    // seconds
  79. const DOWNLOAD_FOREGROUND_INTERVAL  = 0;
  80.  
  81. const UPDATE_WINDOW_NAME      = "Update:Wizard";
  82. const TOOLKIT_ID              = "toolkit@mozilla.org";
  83.  
  84. const POST_UPDATE_CONTRACTID = "@mozilla.org/updates/post-update;1";
  85.  
  86. var gApp        = null;
  87. var gPref       = null;
  88. var gABI        = null;
  89. var gOSVersion  = null;
  90. var gLocale     = null;
  91. var gConsole    = null;
  92. var gCanUpdate  = null;
  93. var gLogEnabled = { };
  94.  
  95. // shared code for suppressing bad cert dialogs
  96. //@line 41 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\shared\src\badCertHandler.js"
  97.  
  98. /**
  99.  * Only allow built-in certs for HTTPS connections.  See bug 340198.
  100.  */
  101. function checkCert(channel) {
  102.   if (!channel.originalURI.schemeIs("https"))  // bypass
  103.     return;
  104.  
  105.   const Ci = Components.interfaces;  
  106.   var cert =
  107.       channel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider).
  108.       SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
  109.  
  110.   var issuer = cert.issuer;
  111.   while (issuer && !cert.equals(issuer)) {
  112.     cert = issuer;
  113.     issuer = cert.issuer;
  114.   }
  115.  
  116.   var errorstring = "cert issuer is not built-in";
  117.   if (!issuer)
  118.     throw errorstring;
  119.  
  120.   issuer = issuer.QueryInterface(Ci.nsIX509Cert3);
  121.   var tokenNames = issuer.getAllTokenNames({});
  122.  
  123.   if (!tokenNames.some(isBuiltinToken))
  124.     throw errorstring;
  125. }
  126.  
  127. function isBuiltinToken(tokenName) {
  128.   return tokenName == "Builtin Object Token";
  129. }
  130.  
  131. /**
  132.  * This class implements nsIBadCertListener.  Its job is to prevent "bad cert"
  133.  * security dialogs from being shown to the user.  It is better to simply fail
  134.  * if the certificate is bad. See bug 304286.
  135.  */
  136. function BadCertHandler() {
  137. }
  138. BadCertHandler.prototype = {
  139.  
  140.   // nsIChannelEventSink
  141.   onChannelRedirect: function(oldChannel, newChannel, flags) {
  142.     // make sure the certificate of the old channel checks out before we follow
  143.     // a redirect from it.  See bug 340198.
  144.     checkCert(oldChannel);
  145.   },
  146.  
  147.   // Suppress any certificate errors
  148.   notifyCertProblem: function(socketInfo, status, targetSite) {
  149.     return true;
  150.   },
  151.  
  152.   // Suppress any ssl errors
  153.   notifySSLError: function(socketInfo, error, targetSite) {
  154.     return true;
  155.   },
  156.  
  157.   // nsIInterfaceRequestor
  158.   getInterface: function(iid) {
  159.     return this.QueryInterface(iid);
  160.   },
  161.  
  162.   // nsISupports
  163.   QueryInterface: function(iid) {
  164.     if (!iid.equals(Components.interfaces.nsIChannelEventSink) &&
  165.         !iid.equals(Components.interfaces.nsIBadCertListener2) &&
  166.         !iid.equals(Components.interfaces.nsISSLErrorListener) &&
  167.         !iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
  168.         !iid.equals(Components.interfaces.nsISupports))
  169.       throw Components.results.NS_ERROR_NO_INTERFACE;
  170.     return this;
  171.   }
  172. };
  173. //@line 137 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  174.  
  175. /**
  176.  * Logs a string to the error console.
  177.  * @param   string
  178.  *          The string to write to the error console..
  179.  */
  180. function LOG(module, string) {
  181.   if (module in gLogEnabled || "all" in gLogEnabled) {
  182.     dump("*** AUS:SVC " + module + ":" + string + "\n");
  183.     // On startup gConsole may not be initialized
  184.     if (gConsole)
  185.       gConsole.logStringMessage("AUS:SVC " + module + ":" + string);
  186.   }
  187. }
  188.  
  189. /**
  190.  * Convert a string containing binary values to hex.
  191.  */
  192. function binaryToHex(input) {
  193.   var result = "";
  194.   for (var i = 0; i < input.length; ++i) {
  195.     var hex = input.charCodeAt(i).toString(16);
  196.     if (hex.length == 1)
  197.       hex = "0" + hex;
  198.     result += hex;
  199.   }
  200.   return result;
  201. }
  202.  
  203. /**
  204.  * Gets a File URL spec for a nsIFile
  205.  * @param   file
  206.  *          The file to get a file URL spec to
  207.  * @returns The file URL spec to the file
  208.  */
  209. function getURLSpecFromFile(file) {
  210.   var ioServ = Cc["@mozilla.org/network/io-service;1"].
  211.                getService(Ci.nsIIOService);
  212.   var fph = ioServ.getProtocolHandler("file").
  213.             QueryInterface(Ci.nsIFileProtocolHandler);
  214.   return fph.getURLSpecFromFile(file);
  215. }
  216.  
  217. /**
  218.  * Gets the specified directory at the specified hierarchy under a
  219.  * Directory Service key.
  220.  * @param   key
  221.  *          The Directory Service Key to start from
  222.  * @param   pathArray
  223.  *          An array of path components to locate beneath the directory
  224.  *          specified by |key|
  225.  * @return  nsIFile object for the location specified. If the directory
  226.  *          requested does not exist, it is created, along with any
  227.  *          parent directories that need to be created.
  228.  */
  229. function getDir(key, pathArray) {
  230.   return getDirInternal(key, pathArray, true, false);
  231. }
  232.  
  233. /**
  234.  * Gets the specified directory at the specified hierarchy under a
  235.  * Directory Service key.
  236.  * @param   key
  237.  *          The Directory Service Key to start from
  238.  * @param   pathArray
  239.  *          An array of path components to locate beneath the directory
  240.  *          specified by |key|
  241.  * @return  nsIFile object for the location specified. If the directory
  242.  *          requested does not exist, it is NOT created.
  243.  */
  244. function getDirNoCreate(key, pathArray) {
  245.   return getDirInternal(key, pathArray, false, false);
  246. }
  247.  
  248. /**
  249.  * Gets the specified directory at the specified hierarchy under the
  250.  * update root directory.
  251.  * @param   pathArray
  252.  *          An array of path components to locate beneath the directory
  253.  *          specified by |key|
  254.  * @return  nsIFile object for the location specified. If the directory
  255.  *          requested does not exist, it is created, along with any
  256.  *          parent directories that need to be created.
  257.  */
  258. function getUpdateDir(pathArray) {
  259.   return getDirInternal(KEY_APPDIR, pathArray, true, true);
  260. }
  261.  
  262. /**
  263.  * Gets the specified directory at the specified hierarchy under a
  264.  * Directory Service key.
  265.  * @param   key
  266.  *          The Directory Service Key to start from
  267.  * @param   pathArray
  268.  *          An array of path components to locate beneath the directory
  269.  *          specified by |key|
  270.  * @param   shouldCreate
  271.  *          true if the directory hierarchy specified in |pathArray|
  272.  *          should be created if it does not exist,
  273.  *          false otherwise.
  274.  * @param   update
  275.  *          true if finding the update directory,
  276.  *          false otherwise.
  277.  * @return  nsIFile object for the location specified.
  278.  */
  279. function getDirInternal(key, pathArray, shouldCreate, update) {
  280.   var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
  281.                     getService(Ci.nsIProperties);
  282.   var dir = fileLocator.get(key, Ci.nsIFile);
  283. //@line 247 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  284.   if (update) {
  285.     try {
  286.       dir = fileLocator.get(KEY_UPDROOT, Ci.nsIFile);
  287.     } catch (e) {
  288.     }
  289.   }
  290. //@line 254 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  291.   for (var i = 0; i < pathArray.length; ++i) {
  292.     dir.append(pathArray[i]);
  293.     if (shouldCreate && !dir.exists())
  294.       dir.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  295.   }
  296.   return dir;
  297. }
  298.  
  299. /**
  300.  * Gets the file at the specified hierarchy under a Directory Service key.
  301.  * @param   key
  302.  *          The Directory Service Key to start from
  303.  * @param   pathArray
  304.  *          An array of path components to locate beneath the directory
  305.  *          specified by |key|. The last item in this array must be the
  306.  *          leaf name of a file.
  307.  * @return  nsIFile object for the file specified. The file is NOT created
  308.  *          if it does not exist, however all required directories along
  309.  *          the way are.
  310.  */
  311. function getFile(key, pathArray) {
  312.   var file = getDir(key, pathArray.slice(0, -1));
  313.   file.append(pathArray[pathArray.length - 1]);
  314.   return file;
  315. }
  316.  
  317. /**
  318.  * Gets the file at the specified hierarchy under the update root directory.
  319.  * @param   pathArray
  320.  *          An array of path components to locate beneath the directory
  321.  *          specified by |key|. The last item in this array must be the
  322.  *          leaf name of a file.
  323.  * @return  nsIFile object for the file specified. The file is NOT created
  324.  *          if it does not exist, however all required directories along
  325.  *          the way are.
  326.  */
  327. function getUpdateFile(pathArray) {
  328.   var file = getUpdateDir(pathArray.slice(0, -1));
  329.   file.append(pathArray[pathArray.length - 1]);
  330.   return file;
  331. }
  332.  
  333. /**
  334.  * Closes a Safe Output Stream
  335.  * @param   fos
  336.  *          The Safe Output Stream to close
  337.  */
  338. function closeSafeOutputStream(fos) {
  339.   if (fos instanceof Ci.nsISafeOutputStream) {
  340.     try {
  341.       fos.finish();
  342.     }
  343.     catch (e) {
  344.       fos.close();
  345.     }
  346.   }
  347.   else
  348.     fos.close();
  349. }
  350.  
  351. /**
  352.  * Returns human readable status text from the updates.properties bundle
  353.  * based on an error code
  354.  * @param   code
  355.  *          The error code to look up human readable status text for
  356.  * @param   defaultCode
  357.  *          The default code to look up should human readable status text
  358.  *          not exist for |code|
  359.  * @returns A human readable status text string
  360.  */
  361. function getStatusTextFromCode(code, defaultCode) {
  362.   const updateBundle = Cc["@mozilla.org/intl/stringbundle;1"].
  363.                        getService(Ci.nsIStringBundleService).
  364.                        createBundle(URI_UPDATES_PROPERTIES);
  365.   var reason;
  366.   try {
  367.     reason = updateBundle.GetStringFromName("check_error-" + code);
  368.     LOG("General", "getStatusTextFromCode - transfer error: " + reason +
  369.         ", code: " + code);
  370.   }
  371.   catch (e) {
  372.     // Use the default reason
  373.     reason = updateBundle.GetStringFromName("check_error-" + defaultCode);
  374.     LOG("General", "getStatusTextFromCode - transfer error: " + reason +
  375.         ", default code: " + defaultCode);
  376.   }
  377.   return reason;
  378. }
  379.  
  380. /**
  381.  * Get the Active Updates directory
  382.  * @param   key
  383.  *          The Directory Service Key (optional).
  384.  *          If used, don't search local appdata on Win32 and don't create dir.
  385.  * @returns The active updates directory, as a nsIFile object
  386.  */
  387. function getUpdatesDir(key) {
  388.   // Right now, we only support downloading one patch at a time, so we always
  389.   // use the same target directory.
  390.   var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
  391.                     getService(Ci.nsIProperties);
  392.   var updateDir;
  393.   if (key)
  394.     updateDir = fileLocator.get(key, Ci.nsIFile);
  395.   else {
  396.     updateDir = fileLocator.get(KEY_APPDIR, Ci.nsIFile);
  397. //@line 361 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  398.     try {
  399.       updateDir = fileLocator.get(KEY_UPDROOT, Ci.nsIFile);
  400.     } catch (e) {
  401.     }
  402. //@line 366 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  403.   }
  404.   updateDir.append(DIR_UPDATES);
  405.   updateDir.append("0");
  406.   if (!updateDir.exists() && !key) {
  407.     LOG("General", "getUpdatesDir - update directory " + updateDir.path +
  408.         " doesn't exist, creating...");
  409.     updateDir.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  410.   }
  411.   return updateDir;
  412. }
  413.  
  414. /**
  415.  * Reads the update state from the update.status file in the specified
  416.  * directory.
  417.  * @param   dir
  418.  *          The dir to look for an update.status file in
  419.  * @returns The status value of the update.
  420.  */
  421. function readStatusFile(dir) {
  422.   var statusFile = dir.clone();
  423.   statusFile.append(FILE_UPDATE_STATUS);
  424.   var status = readStringFromFile(statusFile) || STATE_NONE;
  425.   LOG("General", "readStatusFile - status: " + status + ", path: " +
  426.       statusFile.path);
  427.   return status;
  428. }
  429.  
  430. /**
  431.  * Writes the current update operation/state to a file in the patch
  432.  * directory, indicating to the patching system that operations need
  433.  * to be performed.
  434.  * @param   dir
  435.  *          The patch directory where the update.status file should be
  436.  *          written.
  437.  * @param   state
  438.  *          The state value to write.
  439.  */
  440. function writeStatusFile(dir, state) {
  441.   var statusFile = dir.clone();
  442.   statusFile.append(FILE_UPDATE_STATUS);
  443.   writeStringToFile(statusFile, state);
  444. }
  445.  
  446. /**
  447. //@line 424 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  448.  */
  449. function writeVersionFile(dir, version) {
  450.   var versionFile = dir.clone();
  451.   versionFile.append(FILE_UPDATE_VERSION);
  452.   writeStringToFile(versionFile, version);
  453. }
  454.  
  455. /**
  456.  * Removes the Updates Directory
  457.  * @param   key
  458.  *          The Directory Service Key under which update directory resides
  459.  *          (optional).
  460.  */
  461. function cleanUpUpdatesDir(key) {
  462.   // Bail out if we don't have appropriate permissions
  463.   var updateDir;
  464.   try {
  465.     updateDir = getUpdatesDir(key);
  466.   }
  467.   catch (e) {
  468.     return;
  469.   }
  470.  
  471.   var e = updateDir.directoryEntries;
  472.   while (e.hasMoreElements()) {
  473.     var f = e.getNext().QueryInterface(Ci.nsIFile);
  474.     // Preserve the last update log file for debugging purposes
  475.     if (f.leafName == FILE_UPDATE_LOG) {
  476.       try {
  477.         var dir = f.parent.parent;
  478.         var logFile = dir.clone();
  479.         logFile.append(FILE_LAST_LOG);
  480.         if (logFile.exists())
  481.           logFile.remove(false);
  482.         f.copyTo(dir, FILE_LAST_LOG);
  483.       }
  484.       catch (e) {
  485.         LOG("General", "cleanUpUpdatesDir - failed to copy file: " + f.path);
  486.       }
  487.     }
  488.     // Now, recursively remove this file.  The recusive removal is really
  489.     // only needed on Mac OSX because this directory will contain a copy of
  490.     // updater.app, which is itself a directory.
  491.     try {
  492.       f.remove(true);
  493.     }
  494.     catch (e) {
  495.       LOG("General", "cleanUpUpdatesDir - failed to remove file: " + f.path);
  496.     }
  497.   }
  498.   try {
  499.     updateDir.remove(false);
  500.   } catch (e) {
  501.     LOG("General", "cleanUpUpdatesDir - failed to remove update directory: " +
  502.         updateDir.path + " - This is almost always bad. Exception = " + e);
  503.     throw e;
  504.   }
  505.   LOG("General", "cleanUpUpdatesDir - successfully removed update directory: " +
  506.       updateDir.path);
  507. }
  508.  
  509. /**
  510.  * Clean up updates list and the updates directory.
  511.  * @param   key
  512.  *          The Directory Service Key under which update directory resides
  513.  *          (optional).
  514.  */
  515. function cleanupActiveUpdate(key) {
  516.   // Move the update from the Active Update list into the Past Updates list.
  517.   var um = Cc["@mozilla.org/updates/update-manager;1"].
  518.            getService(Ci.nsIUpdateManager);
  519.   um.activeUpdate = null;
  520.   um.saveUpdates();
  521.  
  522.   // Now trash the updates directory, since we're done with it
  523.   cleanUpUpdatesDir(key);
  524. }
  525.  
  526. /**
  527.  * Gets a preference value, handling the case where there is no default.
  528.  * @param   func
  529.  *          The name of the preference function to call, on nsIPrefBranch
  530.  * @param   preference
  531.  *          The name of the preference
  532.  * @param   defaultValue
  533.  *          The default value to return in the event the preference has
  534.  *          no setting
  535.  * @returns The value of the preference, or undefined if there was no
  536.  *          user or default value.
  537.  */
  538. function getPref(func, preference, defaultValue) {
  539.   try {
  540.     return gPref[func](preference);
  541.   }
  542.   catch (e) {
  543.   }
  544.   return defaultValue;
  545. }
  546.  
  547. /**
  548.  * Gets the locale from the update.locale file for replacing %LOCALE% in the
  549.  * update url. The update.locale file can be located in the application
  550.  * directory or the GRE directory with preference given to it being located in
  551.  * the application directory.
  552.  */
  553. function getLocale() {
  554.   if (gLocale)
  555.     return gLocale;
  556.  
  557.   var localeFile = getFile(KEY_APPDIR, [FILE_UPDATE_LOCALE]);
  558.   if (!localeFile.exists())
  559.     localeFile = getFile(KEY_GRED, [FILE_UPDATE_LOCALE]);
  560.  
  561.   if (!localeFile.exists())
  562.     throw Components.Exception(FILE_UPDATE_LOCALE + " file doesn't exist in " +
  563.                                "either the " + KEY_APPDIR + " or " + KEY_GRED +
  564.                                " directories", Cr.NS_ERROR_FILE_NOT_FOUND);
  565.  
  566.   gLocale = readStringFromFile(localeFile);
  567.   LOG("General", "getLocale - getting locale from file: " + localeFile.path +
  568.       ", locale: " + gLocale);
  569.   return gLocale;
  570. }
  571.  
  572. /**
  573.  * Read the update channel from defaults only.  We do this to ensure that
  574.  * the channel is tightly coupled with the application and does not apply
  575.  * to other instances of the application that may use the same profile.
  576.  */
  577. function getUpdateChannel() {
  578.   var channel = "default";
  579.   var prefName;
  580.   var prefValue;
  581.  
  582.   try {
  583.     channel = getDefaultPrefBranch().getCharPref(PREF_APP_UPDATE_CHANNEL);
  584.   } catch (e) {
  585.     // use default when pref not found
  586.   }
  587.  
  588.   try {
  589.     var partners = gPref.getChildList(PREF_PARTNER_BRANCH, { });
  590.     if (partners.length) {
  591.       channel += "-cck";
  592.       partners.sort();
  593.  
  594.       for each (prefName in partners) {
  595.         prefValue = gPref.getCharPref(prefName);
  596.         channel += "-" + prefValue;
  597.       }
  598.     }
  599.   }
  600.   catch (e) {
  601.     Components.utils.reportError(e);
  602.   }
  603.  
  604.   return channel;
  605. }
  606.  
  607. /* Get the distribution pref values, from defaults only */
  608. function getDistributionPrefValue(aPrefName) {
  609.   var prefValue = "default";
  610.  
  611.   try {
  612.     prefValue = getDefaultPrefBranch().getCharPref(aPrefName);
  613.   } catch (e) {
  614.     // use default when pref not found
  615.   }
  616.  
  617.   return prefValue;
  618. }
  619.  
  620. /**
  621.  * An enumeration of items in a JS array.
  622.  * @constructor
  623.  */
  624. function ArrayEnumerator(aItems) {
  625.   this._index = 0;
  626.   if (aItems) {
  627.     for (var i = 0; i < aItems.length; ++i) {
  628.       if (!aItems[i])
  629.         aItems.splice(i, 1);
  630.     }
  631.   }
  632.   this._contents = aItems;
  633. }
  634.  
  635. ArrayEnumerator.prototype = {
  636.   _index: 0,
  637.   _contents: [],
  638.  
  639.   hasMoreElements: function ArrayEnumerator_hasMoreElements() {
  640.     return this._index < this._contents.length;
  641.   },
  642.  
  643.   getNext: function ArrayEnumerator_getNext() {
  644.     return this._contents[this._index++];
  645.   }
  646. };
  647.  
  648. /**
  649.  * Trims a prefix from a string.
  650.  * @param   string
  651.  *          The source string
  652.  * @param   prefix
  653.  *          The prefix to remove.
  654.  * @returns The suffix (string - prefix)
  655.  */
  656. function stripPrefix(string, prefix) {
  657.   return string.substr(prefix.length);
  658. }
  659.  
  660. /**
  661.  * Writes a string of text to a file.  A newline will be appended to the data
  662.  * written to the file.  This function only works with ASCII text.
  663.  */
  664. function writeStringToFile(file, text) {
  665.   var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"].
  666.             createInstance(Ci.nsIFileOutputStream);
  667.   var modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
  668.   if (!file.exists())
  669.     file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  670.   fos.init(file, modeFlags, PERMS_FILE, 0);
  671.   text += "\n";
  672.   fos.write(text, text.length);
  673.   closeSafeOutputStream(fos);
  674. }
  675.  
  676. /**
  677.  * Reads a string of text from a file.  A trailing newline will be removed
  678.  * before the result is returned.  This function only works with ASCII text.
  679.  */
  680. function readStringFromFile(file) {
  681.   var fis = Cc["@mozilla.org/network/file-input-stream;1"].
  682.             createInstance(Ci.nsIFileInputStream);
  683.   var modeFlags = MODE_RDONLY;
  684.   if (!file.exists()) {
  685.     LOG("General", "readStringFromFile - file doesn't exist: " + file.path);
  686.     return null;
  687.   }
  688.   fis.init(file, modeFlags, PERMS_FILE, 0);
  689.   var sis = Cc["@mozilla.org/scriptableinputstream;1"].
  690.             createInstance(Ci.nsIScriptableInputStream);
  691.   sis.init(fis);
  692.   var text = sis.read(sis.available());
  693.   sis.close();
  694.   if (text[text.length - 1] == "\n")
  695.     text = text.slice(0, -1);
  696.   return text;
  697. }
  698.  
  699. function getObserverService()
  700. {
  701.   return Cc["@mozilla.org/observer-service;1"].
  702.          getService(Ci.nsIObserverService);
  703. }
  704.  
  705. function getDefaultPrefBranch()
  706. {
  707.   return gPref.QueryInterface(Ci.nsIPrefService).getDefaultBranch(null);
  708. }
  709. /**
  710.  * Update Patch
  711.  * @param   patch
  712.  *          A <patch> element to initialize this object with
  713.  * @throws if patch has a size of 0
  714.  * @constructor
  715.  */
  716. function UpdatePatch(patch) {
  717.   this._properties = {};
  718.   for (var i = 0; i < patch.attributes.length; ++i) {
  719.     var attr = patch.attributes.item(i);
  720.     attr.QueryInterface(Ci.nsIDOMAttr);
  721.     switch (attr.name) {
  722.     case "selected":
  723.       this.selected = attr.value == "true";
  724.       break;
  725.     case "size":
  726.       if (0 == parseInt(attr.value)) {
  727.         LOG("UpdatePatch", "init - 0-sized patch!");
  728.         throw Cr.NS_ERROR_ILLEGAL_VALUE;
  729.       }
  730.       // fall through
  731.     default:
  732.       this[attr.name] = attr.value;
  733.       break;
  734.     };
  735.   }
  736. }
  737. UpdatePatch.prototype = {
  738.   /**
  739.    * See nsIUpdateService.idl
  740.    */
  741.   serialize: function UpdatePatch_serialize(updates) {
  742.     var patch = updates.createElementNS(URI_UPDATE_NS, "patch");
  743.     patch.setAttribute("type", this.type);
  744.     patch.setAttribute("URL", this.URL);
  745.     patch.setAttribute("hashFunction", this.hashFunction);
  746.     patch.setAttribute("hashValue", this.hashValue);
  747.     patch.setAttribute("size", this.size);
  748.     patch.setAttribute("selected", this.selected);
  749.     patch.setAttribute("state", this.state);
  750.  
  751.     for (var p in this._properties) {
  752.       if (this._properties[p].present)
  753.         patch.setAttribute(p, this._properties[p].data);
  754.     }
  755.  
  756.     return patch;
  757.   },
  758.  
  759.   /**
  760.    * A hash of custom properties
  761.    */
  762.   _properties: null,
  763.  
  764.   /**
  765.    * See nsIWritablePropertyBag.idl
  766.    */
  767.   setProperty: function UpdatePatch_setProperty(name, value) {
  768.     this._properties[name] = { data: value, present: true };
  769.   },
  770.  
  771.   /**
  772.    * See nsIWritablePropertyBag.idl
  773.    */
  774.   deleteProperty: function UpdatePatch_deleteProperty(name) {
  775.     if (name in this._properties)
  776.       this._properties[name].present = false;
  777.     else
  778.       throw Cr.NS_ERROR_FAILURE;
  779.   },
  780.  
  781.   /**
  782.    * See nsIPropertyBag.idl
  783.    */
  784.   get enumerator() {
  785.     var properties = [];
  786.     for (var p in this._properties)
  787.       properties.push(this._properties[p].data);
  788.     return new ArrayEnumerator(properties);
  789.   },
  790.  
  791.   /**
  792.    * See nsIPropertyBag.idl
  793.    */
  794.   getProperty: function UpdatePatch_getProperty(name) {
  795.     if (name in this._properties &&
  796.         this._properties[name].present)
  797.       return this._properties[name].data;
  798.     throw Cr.NS_ERROR_FAILURE;
  799.   },
  800.  
  801.   /**
  802.    * Returns whether or not the update.status file for this patch exists at the
  803.    * appropriate location.
  804.    */
  805.   get statusFileExists() {
  806.     var statusFile = getUpdatesDir();
  807.     statusFile.append(FILE_UPDATE_STATUS);
  808.     return statusFile.exists();
  809.   },
  810.  
  811.   /**
  812.    * See nsIUpdateService.idl
  813.    */
  814.   get state() {
  815.     if (this._properties.state)
  816.       return this._properties.state;
  817.     return STATE_NONE;
  818.   },
  819.   set state(val) {
  820.     this._properties.state = val;
  821.   },
  822.  
  823.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePatch,
  824.                                          Ci.nsIPropertyBag,
  825.                                          Ci.nsIWritablePropertyBag])
  826. };
  827.  
  828. /**
  829.  * Update
  830.  * Implements nsIUpdate
  831.  * @param   update
  832.  *          An <update> element to initialize this object with
  833.  * @throws if the update contains no patches
  834.  * @constructor
  835.  */
  836. function Update(update) {
  837.   this._properties = {};
  838.   this._patches = [];
  839.   this.installDate = 0;
  840.   this.isCompleteUpdate = false;
  841.   this.channel = "default"
  842.  
  843.   // Null <update>, assume this is a message container and do no
  844.   // further initialization
  845.   if (!update)
  846.     return;
  847.  
  848.   const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
  849.   for (var i = 0; i < update.childNodes.length; ++i) {
  850.     var patchElement = update.childNodes.item(i);
  851.     if (patchElement.nodeType != ELEMENT_NODE ||
  852.         patchElement.localName != "patch")
  853.       continue;
  854.  
  855.     patchElement.QueryInterface(Ci.nsIDOMElement);
  856.     try {
  857.       var patch = new UpdatePatch(patchElement);
  858.     } catch (e) {
  859.       continue;
  860.     }
  861.     this._patches.push(patch);
  862.   }
  863.  
  864.   if (0 == this._patches.length)
  865.     throw Cr.NS_ERROR_ILLEGAL_VALUE;
  866.  
  867.   for (var i = 0; i < update.attributes.length; ++i) {
  868.     var attr = update.attributes.item(i);
  869.     attr.QueryInterface(Ci.nsIDOMAttr);
  870.     if (attr.name == "installDate" && attr.value)
  871.       this.installDate = parseInt(attr.value);
  872.     else if (attr.name == "isCompleteUpdate")
  873.       this.isCompleteUpdate = attr.value == "true";
  874.     else if (attr.name == "isSecurityUpdate")
  875.       this.isSecurityUpdate = attr.value == "true";
  876.     else if (attr.name == "detailsURL")
  877.       this._detailsURL = attr.value;
  878.     else if (attr.name == "channel")
  879.       this.channel = attr.value;
  880.     else
  881.       this[attr.name] = attr.value;
  882.   }
  883.  
  884.   // The Update Name is either the string provided by the <update> element, or
  885.   // the string: "<App Name> <Update App Version>"
  886.   var name = "";
  887.   if (update.hasAttribute("name"))
  888.     name = update.getAttribute("name");
  889.   else {
  890.     var sbs = Cc["@mozilla.org/intl/stringbundle;1"].
  891.               getService(Ci.nsIStringBundleService);
  892.     var brandBundle = sbs.createBundle(URI_BRAND_PROPERTIES);
  893.     var updateBundle = sbs.createBundle(URI_UPDATES_PROPERTIES);
  894.     var appName = brandBundle.GetStringFromName("brandShortName");
  895.     name = updateBundle.formatStringFromName("updateName",
  896.                                              [appName, this.version], 2);
  897.   }
  898.   this.name = name;
  899. }
  900. Update.prototype = {
  901.   /**
  902.    * See nsIUpdateService.idl
  903.    */
  904.   get patchCount() {
  905.     return this._patches.length;
  906.   },
  907.  
  908.   /**
  909.    * See nsIUpdateService.idl
  910.    */
  911.   getPatchAt: function Update_getPatchAt(index) {
  912.     return this._patches[index];
  913.   },
  914.  
  915.   /**
  916.    * See nsIUpdateService.idl
  917.    *
  918.    * We use a copy of the state cached on this object in |_state| only when
  919.    * there is no selected patch, i.e. in the case when we could not load
  920.    * |.activeUpdate| from the update manager for some reason but still have
  921.    * the update.status file to work with.
  922.    */
  923.   _state: "",
  924.   set state(state) {
  925.     if (this.selectedPatch)
  926.       this.selectedPatch.state = state;
  927.     this._state = state;
  928.     return state;
  929.   },
  930.   get state() {
  931.     if (this.selectedPatch)
  932.       return this.selectedPatch.state;
  933.     return this._state;
  934.   },
  935.  
  936.   /**
  937.    * See nsIUpdateService.idl
  938.    */
  939.   errorCode: 0,
  940.  
  941.   /**
  942.    * See nsIUpdateService.idl
  943.    */
  944.   get selectedPatch() {
  945.     for (var i = 0; i < this.patchCount; ++i) {
  946.       if (this._patches[i].selected)
  947.         return this._patches[i];
  948.     }
  949.     return null;
  950.   },
  951.  
  952.   /**
  953.    * See nsIUpdateService.idl
  954.    */
  955.   get detailsURL() {
  956.     if (!this._detailsURL) {
  957.       try {
  958.         // Try using a default details URL supplied by the distribution
  959.         // if the update XML does not supply one.
  960.         var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
  961.                         getService(Ci.nsIURLFormatter);
  962.         return formatter.formatURLPref(PREF_APP_UPDATE_URL_DETAILS);
  963.       }
  964.       catch (e) {
  965.       }
  966.     }
  967.     return this._detailsURL || "";
  968.   },
  969.  
  970.   /**
  971.    * See nsIUpdateService.idl
  972.    */
  973.   serialize: function Update_serialize(updates) {
  974.     var update = updates.createElementNS(URI_UPDATE_NS, "update");
  975.     update.setAttribute("type", this.type);
  976.     update.setAttribute("name", this.name);
  977.     update.setAttribute("version", this.version);
  978.     update.setAttribute("platformVersion", this.platformVersion);
  979.     update.setAttribute("extensionVersion", this.extensionVersion);
  980.     update.setAttribute("detailsURL", this.detailsURL);
  981.     update.setAttribute("licenseURL", this.licenseURL);
  982.     update.setAttribute("serviceURL", this.serviceURL);
  983.     update.setAttribute("installDate", this.installDate);
  984.     update.setAttribute("statusText", this.statusText);
  985.     update.setAttribute("buildID", this.buildID);
  986.     update.setAttribute("isCompleteUpdate", this.isCompleteUpdate);
  987.     update.setAttribute("channel", this.channel);
  988.     updates.documentElement.appendChild(update);
  989.  
  990.     for (var p in this._properties) {
  991.       if (this._properties[p].present)
  992.         update.setAttribute(p, this._properties[p].data);
  993.     }
  994.  
  995.     for (var i = 0; i < this.patchCount; ++i)
  996.       update.appendChild(this.getPatchAt(i).serialize(updates));
  997.  
  998.     return update;
  999.   },
  1000.  
  1001.   /**
  1002.    * A hash of custom properties
  1003.    */
  1004.   _properties: null,
  1005.  
  1006.   /**
  1007.    * See nsIWritablePropertyBag.idl
  1008.    */
  1009.   setProperty: function Update_setProperty(name, value) {
  1010.     this._properties[name] = { data: value, present: true };
  1011.   },
  1012.  
  1013.   /**
  1014.    * See nsIWritablePropertyBag.idl
  1015.    */
  1016.   deleteProperty: function Update_deleteProperty(name) {
  1017.     if (name in this._properties)
  1018.       this._properties[name].present = false;
  1019.     else
  1020.       throw Cr.NS_ERROR_FAILURE;
  1021.   },
  1022.  
  1023.   /**
  1024.    * See nsIPropertyBag.idl
  1025.    */
  1026.   get enumerator() {
  1027.     var properties = [];
  1028.     for (var p in this._properties)
  1029.       properties.push(this._properties[p].data);
  1030.     return new ArrayEnumerator(properties);
  1031.   },
  1032.  
  1033.   /**
  1034.    * See nsIPropertyBag.idl
  1035.    */
  1036.   getProperty: function Update_getProperty(name) {
  1037.     if (name in this._properties &&
  1038.         this._properties[name].present)
  1039.       return this._properties[name].data;
  1040.     throw Cr.NS_ERROR_FAILURE;
  1041.   },
  1042.  
  1043.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdate,
  1044.                                          Ci.nsIPropertyBag,
  1045.                                          Ci.nsIWritablePropertyBag])
  1046. };
  1047.  
  1048. const UpdateServiceFactory = {
  1049.   _instance: null,
  1050.   createInstance: function (outer, iid) {
  1051.     if (outer != null)
  1052.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  1053.     return this._instance == null ? this._instance = new UpdateService() :
  1054.                                     this._instance;
  1055.   }
  1056. };
  1057.  
  1058. /**
  1059.  * UpdateService
  1060.  * A Service for managing the discovery and installation of software updates.
  1061.  * @constructor
  1062.  */
  1063. function UpdateService() {
  1064.   gApp  = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
  1065.           QueryInterface(Ci.nsIXULRuntime);
  1066.   gPref = Cc["@mozilla.org/preferences-service;1"].
  1067.           getService(Ci.nsIPrefBranch2);
  1068.   gConsole = Cc["@mozilla.org/consoleservice;1"].
  1069.              getService(Ci.nsIConsoleService);
  1070.  
  1071.   // Not all builds have a known ABI
  1072.   try {
  1073.     gABI = gApp.XPCOMABI;
  1074.   }
  1075.   catch (e) {
  1076.     LOG("UpdateService", "init - XPCOM ABI unknown: updates are not possible.");
  1077.   }
  1078.  
  1079.   var osVersion;
  1080.   var sysInfo = Cc["@mozilla.org/system-info;1"].
  1081.                 getService(Ci.nsIPropertyBag2);
  1082.   try {
  1083.     osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
  1084.   }
  1085.   catch (e) {
  1086.     LOG("UpdateService", "init - OS Version unknown: updates are not possible.");
  1087.   }
  1088.  
  1089.   if (osVersion) {
  1090.     try {
  1091.       osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
  1092.     }
  1093.     catch (e) {
  1094.       // Not all platforms have a secondary widget library, so an error is nothing to worry about.
  1095.     }
  1096.     gOSVersion = encodeURIComponent(osVersion);
  1097.   }
  1098.  
  1099. //@line 1084 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1100.  
  1101.   // Start the update timer only after a profile has been selected so that the
  1102.   // appropriate values for the update check are read from the user's profile.
  1103.   var os = getObserverService();
  1104.  
  1105.   os.addObserver(this, "profile-after-change", false);
  1106.  
  1107.   // Observe xpcom-shutdown to unhook pref branch observers above to avoid
  1108.   // shutdown leaks.
  1109.   os.addObserver(this, "xpcom-shutdown", false);
  1110. }
  1111.  
  1112. UpdateService.prototype = {
  1113.   /**
  1114.    * The downloader we are using to download updates. There is only ever one of
  1115.    * these.
  1116.    */
  1117.   _downloader: null,
  1118.  
  1119.   /**
  1120.    * Incompatible add-on count.
  1121.    */
  1122.   _incompatAddonsCount: 0,
  1123.  
  1124.   /**
  1125.    * Handle Observer Service notifications
  1126.    * @param   subject
  1127.    *          The subject of the notification
  1128.    * @param   topic
  1129.    *          The notification name
  1130.    * @param   data
  1131.    *          Additional data
  1132.    */
  1133.   observe: function AUS_observe(subject, topic, data) {
  1134.     var os = getObserverService();
  1135.  
  1136.     switch (topic) {
  1137.     case "profile-after-change":
  1138.       os.removeObserver(this, "profile-after-change");
  1139.       os.addObserver(this, "final-ui-startup", false);
  1140.       this._start();
  1141.       break;
  1142.     case "final-ui-startup":
  1143.       os.removeObserver(this, "final-ui-startup");
  1144.       this._final_ui_start();
  1145.       break;
  1146.     case "xpcom-shutdown":
  1147.       os.removeObserver(this, "xpcom-shutdown");
  1148.  
  1149.       // Prevent leaking the downloader (bug 454964)
  1150.       this._downloader = null;
  1151.  
  1152.       // Release Services
  1153.       gApp      = null;
  1154.       gPref     = null;
  1155.       gConsole  = null;
  1156.       break;
  1157.     }
  1158.   },
  1159.  
  1160.   /**
  1161.    * Start the Update Service
  1162.    */
  1163.   _start: function AUS__start() {
  1164.     // Start logging
  1165.     this._initLoggingPrefs();
  1166.  
  1167.     // Register a background update check timer
  1168.     var tm = Cc["@mozilla.org/updates/timer-manager;1"].
  1169.              getService(Ci.nsIUpdateTimerManager);
  1170.     var interval = getPref("getIntPref", PREF_APP_UPDATE_INTERVAL, 86400);
  1171.     tm.registerTimer("background-update-timer", this, interval);
  1172.   },
  1173.  
  1174.   /**
  1175.    * The following needs to be performed after final-ui-startup (bug 497578)
  1176.    * 1. post update processing
  1177.    * 2. resume of a download that was in progress during a previous session
  1178.    * 3. start of a complete update download after the failure to apply a partial
  1179.    *    update
  1180.    */
  1181.   _final_ui_start: function AUS__delayed_start() {
  1182.     // Clean up any extant updates
  1183.     this._postUpdateProcessing();
  1184.  
  1185.     // Resume fetching...
  1186.     var um = Cc["@mozilla.org/updates/update-manager;1"].
  1187.              getService(Ci.nsIUpdateManager);
  1188.     var activeUpdate = um.activeUpdate;
  1189.     if (activeUpdate && activeUpdate.state != STATE_SUCCEEDED) {
  1190.       var status = this.downloadUpdate(activeUpdate, true);
  1191.       if (status == STATE_NONE)
  1192.         cleanupActiveUpdate();
  1193.     }
  1194.   },
  1195.  
  1196.   /**
  1197.    * Perform post-processing on updates lingering in the updates directory
  1198.    * from a previous browser session - either report install failures (and
  1199.    * optionally attempt to fetch a different version if appropriate) or
  1200.    * notify the user of install success.
  1201.    */
  1202.   _postUpdateProcessing: function AUS__postUpdateProcessing() {
  1203.     // Detect installation failures and notify
  1204.  
  1205.     // Bail out if we don't have appropriate permissions
  1206.     if (!this.canUpdate)
  1207.       return;
  1208.  
  1209.     var status = readStatusFile(getUpdatesDir());
  1210.  
  1211.     // Make sure to cleanup after an update that failed for an unknown reason
  1212.     if (status == "null")
  1213.       status = null;
  1214.  
  1215.     var updRootKey = null;
  1216. //@line 1201 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1217.     function findPreviousUpdate(key) {
  1218.       var updateDir = getUpdatesDir(key);
  1219.       if (updateDir.exists()) {
  1220.         status = readStatusFile(updateDir);
  1221.         // Previous download should succeed. Otherwise, we will not be here!
  1222.         if (status == STATE_SUCCEEDED)
  1223.           updRootKey = key;
  1224.         else
  1225.           status = null;
  1226.       }
  1227.     }
  1228.  
  1229.     // required when updating from Fx 2.0.0.1 to 2.0.0.3 (or later)
  1230.     // on Windows Vista.
  1231.     if (status == null)
  1232.       findPreviousUpdate(KEY_UAPPDATA);
  1233.  
  1234.     // required to migrate from older versions.
  1235.     if (status == null)
  1236.       findPreviousUpdate(KEY_APPDIR);
  1237. //@line 1222 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1238.  
  1239.     if (status == STATE_DOWNLOADING) {
  1240.       LOG("UpdateService", "_postUpdateProcessing - patch found in " +
  1241.           "downloading state");
  1242.     }
  1243.     else if (status != null) {
  1244.       // null status means the update.status file is not present, because either:
  1245.       // 1) no update was performed, and so there's no UI to show
  1246.       // 2) an update was attempted but failed during checking, transfer or
  1247.       //    verification, and was cleaned up at that point, and UI notifying of
  1248.       //    that error was shown at that stage.
  1249.       var um = Cc["@mozilla.org/updates/update-manager;1"].
  1250.                getService(Ci.nsIUpdateManager);
  1251.       var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
  1252.                      createInstance(Ci.nsIUpdatePrompt);
  1253.  
  1254.       var update = um.activeUpdate;
  1255.       if (!update) {
  1256.         update = new Update(null);
  1257.       }
  1258.       update.state = status;
  1259.       var sbs = Cc["@mozilla.org/intl/stringbundle;1"].
  1260.                 getService(Ci.nsIStringBundleService);
  1261.       var bundle = sbs.createBundle(URI_UPDATES_PROPERTIES);
  1262.       if (status == STATE_SUCCEEDED) {
  1263.         update.statusText = bundle.GetStringFromName("installSuccess");
  1264.  
  1265.         // Update the patch's metadata.
  1266.         um.activeUpdate = update;
  1267.  
  1268.         prompter.showUpdateInstalled();
  1269. //@line 1257 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1270.         // Perform platform-specific post-update processing.
  1271.         if (POST_UPDATE_CONTRACTID in Cc) {
  1272.           Cc[POST_UPDATE_CONTRACTID].createInstance(Ci.nsIRunnable).run();
  1273.         }
  1274. //@line 1262 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1275.         // Done with this update. Clean it up.
  1276.         cleanupActiveUpdate(updRootKey);
  1277.       }
  1278.       else {
  1279.         // If we hit an error, then the error code will be included in the
  1280.         // status string following a colon.  If we had an I/O error, then we
  1281.         // assume that the patch is not invalid, and we restage the patch so
  1282.         // that it can be attempted again the next time we restart.
  1283.         var ary = status.split(": ");
  1284.         update.state = ary[0];
  1285.         if (update.state == STATE_FAILED && ary[1]) {
  1286.           update.errorCode = ary[1];
  1287.           if (update.errorCode == WRITE_ERROR) {
  1288.             prompter.showUpdateError(update);
  1289.             writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
  1290.             writeVersionFile(getUpdatesDir(), update.extensionVersion);
  1291.             return;
  1292.           }
  1293.         }
  1294.  
  1295.         // Something went wrong with the patch application process.
  1296.         cleanupActiveUpdate();
  1297.  
  1298.         update.statusText = bundle.GetStringFromName("patchApplyFailure");
  1299.         var oldType = update.selectedPatch ? update.selectedPatch.type
  1300.                                            : "complete";
  1301.         if (update.selectedPatch && oldType == "partial") {
  1302.           // Partial patch application failed, try downloading the complete
  1303.           // update in the background instead.
  1304.           LOG("UpdateService", "_postUpdateProcessing - install of partial " +
  1305.               "patch failed, downloading complete patch");
  1306.           var status = this.downloadUpdate(update, true);
  1307.           if (status == STATE_NONE)
  1308.             cleanupActiveUpdate();
  1309.         }
  1310.         else {
  1311.           LOG("UpdateService", "_postUpdateProcessing - install of complete or " +
  1312.               "only one patch offered failed... showing error.");
  1313.         }
  1314.         update.QueryInterface(Ci.nsIWritablePropertyBag);
  1315.         update.setProperty("patchingFailed", oldType);
  1316.         prompter.showUpdateError(update);
  1317.       }
  1318.     }
  1319.     else {
  1320.       LOG("UpdateService", "_postUpdateProcessing - no status, no update");
  1321.     }
  1322.   },
  1323.  
  1324.   /**
  1325.    * Initialize Logging preferences, formatted like so:
  1326.    *  app.update.log.<moduleName> = <true|false>
  1327.    */
  1328.   _initLoggingPrefs: function AUS__initLoggingPrefs() {
  1329.     try {
  1330.       var ps = Cc["@mozilla.org/preferences-service;1"].
  1331.                getService(Ci.nsIPrefService);
  1332.       var logBranch = ps.getBranch(PREF_APP_UPDATE_LOG_BRANCH);
  1333.       var modules = logBranch.getChildList("", { value: 0 });
  1334.  
  1335.       for (var i = 0; i < modules.length; ++i) {
  1336.         if (logBranch.prefHasUserValue(modules[i]))
  1337.           gLogEnabled[modules[i]] = logBranch.getBoolPref(modules[i]);
  1338.       }
  1339.     }
  1340.     catch (e) {
  1341.     }
  1342.   },
  1343.  
  1344.   /**
  1345.    * Notified when a timer fires
  1346.    * @param   timer
  1347.    *          The timer that fired
  1348.    */
  1349.   notify: function AUS_notify(timer) {
  1350.     // If a download is in progress or the patch has been staged do nothing.
  1351.     if (this.isDownloading || this._downloader && this._downloader.patchIsStaged)
  1352.       return;
  1353.  
  1354.     var self = this;
  1355.     var listener = {
  1356.       /**
  1357.        * See nsIUpdateService.idl
  1358.        */
  1359.       onProgress: function AUS_notify_onProgress(request, position, totalSize) {
  1360.       },
  1361.  
  1362.       /**
  1363.        * See nsIUpdateService.idl
  1364.        */
  1365.       onCheckComplete: function AUS_notify_onCheckComplete(request, updates,
  1366.                                                            updateCount) {
  1367.         self._selectAndInstallUpdate(updates);
  1368.       },
  1369.  
  1370.       /**
  1371.        * See nsIUpdateService.idl
  1372.        */
  1373.       onError: function AUS_notify_onError(request, update) {
  1374.         LOG("UpdateService", "notify:listener - error during background " +
  1375.             "update: " + update.statusText);
  1376.       },
  1377.     }
  1378.     this.backgroundChecker.checkForUpdates(listener, false);
  1379.   },
  1380.  
  1381.   /**
  1382.    * Determine which of the specified updates should be installed.
  1383.    * @param   updates
  1384.    *          An array of available updates
  1385.    */
  1386.   selectUpdate: function AUS_selectUpdate(updates) {
  1387.     if (updates.length == 0)
  1388.       return null;
  1389.  
  1390.     // Choose the newest of the available minor and major updates.
  1391.     var majorUpdate = null, minorUpdate = null;
  1392.     var newestMinor = updates[0], newestMajor = updates[0];
  1393.  
  1394.     var vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
  1395.              getService(Ci.nsIVersionComparator);
  1396.     for (var i = 0; i < updates.length; ++i) {
  1397.       if (updates[i].type == "major" &&
  1398.           vc.compare(newestMajor.version, updates[i].version) <= 0)
  1399.         majorUpdate = newestMajor = updates[i];
  1400.       if (updates[i].type == "minor" &&
  1401.           vc.compare(newestMinor.version, updates[i].version) <= 0)
  1402.         minorUpdate = newestMinor = updates[i];
  1403.     }
  1404.  
  1405.     // IMPORTANT
  1406.     // If there's a minor update, always try and fetch that one first,
  1407.     // otherwise use the newest major update.
  1408.     // selectUpdate() only returns one update.
  1409.     // if major were to trump minor, and we said "never" to the major
  1410.     // we'd never get the minor update, since selectUpdate()
  1411.     // would return the major update that the user said "never" to
  1412.     // (shadowing the important minor update with security fixes)
  1413.     return minorUpdate || majorUpdate;
  1414.   },
  1415.  
  1416.   /**
  1417.    * Reference to the currently selected update for when add-on compatibility
  1418.    * is checked.
  1419.    */
  1420.   _update: null,
  1421.  
  1422.   /**
  1423.    * Determine which of the specified updates should be installed and begin the
  1424.    * download/installation process or notify the user about the update.
  1425.    * @param   updates
  1426.    *          An array of available updates
  1427.    */
  1428.   _selectAndInstallUpdate: function AUS__selectAndInstallUpdate(updates) {
  1429.     // Return early if there's an active update. The user is already aware and
  1430.     // is downloading or performed some user action to prevent notification.
  1431.     var um = Cc["@mozilla.org/updates/update-manager;1"].
  1432.              getService(Ci.nsIUpdateManager);
  1433.     if (um.activeUpdate)
  1434.       return;
  1435.  
  1436.     var update = this.selectUpdate(updates, updates.length);
  1437.     if (!update)
  1438.       return;
  1439.  
  1440.     var updateEnabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
  1441.     if (!updateEnabled) {
  1442.       LOG("Checker", "_selectAndInstallUpdate - not prompting because update " +
  1443.           "is disabled");
  1444.       return;
  1445.     }
  1446.  
  1447.     /**
  1448. //@line 1447 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1449.      */
  1450.  
  1451.     // Encode version since it could be a non-ascii string (bug 359093)
  1452.     var neverPrefName = PREF_UPDATE_NEVER_BRANCH +
  1453.                         encodeURIComponent(update.version);
  1454.     if (update.type == "major" &&
  1455.         getPref("getBoolPref", neverPrefName, false)) {
  1456.       LOG("Checker", "_selectAndInstallUpdate - not prompting because this " +
  1457.           "is a major update and the preference " + neverPrefName + " is true");
  1458.       return;
  1459.     }
  1460.  
  1461.     /**
  1462. //@line 1476 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1463.      */
  1464.     if (update.type == "major") {
  1465.       LOG("Checker", "_selectAndInstallUpdate - prompting because it is a " +
  1466.           "major update");
  1467.       this._showPrompt(update);
  1468.       return;
  1469.     }
  1470.  
  1471.     if (!getPref("getBoolPref", PREF_APP_UPDATE_AUTO, true)) {
  1472.       LOG("Checker", "_selectAndInstallUpdate - prompting because silent " +
  1473.           "install is disabled");
  1474.       this._showPrompt(update);
  1475.       return;
  1476.     }
  1477.  
  1478.     if (getPref("getIntPref", PREF_APP_UPDATE_MODE, 1) == 0) {
  1479.       // Do not prompt regardless of add-on incompatibilities
  1480.       LOG("UpdateService", "_selectAndInstallUpdate - no need to show " +
  1481.           "prompt, just download the update");
  1482.       var status = this.downloadUpdate(update, true);
  1483.       if (status == STATE_NONE)
  1484.         cleanupActiveUpdate();
  1485.       return;
  1486.     }
  1487.  
  1488.     var ai = Cc["@mozilla.org/xre/app-info;1"].
  1489.              getService(Ci.nsIXULAppInfo);
  1490.     var vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
  1491.              getService(Ci.nsIVersionComparator);
  1492.     if (update.extensionVersion &&
  1493.         vc.compare(update.extensionVersion, ai.version) != 0) {
  1494.       this._update = update;
  1495.       this._checkAddonCompatibility();
  1496.     }
  1497.     else {
  1498.       LOG("UpdateService", "_selectAndInstallUpdate - no need to show " +
  1499.           "prompt, just download the update");
  1500.       var status = this.downloadUpdate(update, true);
  1501.       if (status == STATE_NONE)
  1502.         cleanupActiveUpdate();
  1503.     }
  1504.   },
  1505.  
  1506.   _showPrompt: function AUS__showPrompt(update) {
  1507.     var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
  1508.                    createInstance(Ci.nsIUpdatePrompt);
  1509.     prompter.showUpdateAvailable(update);
  1510.   },
  1511.  
  1512.   _checkAddonCompatibility: function AUS__checkAddonCompatibility() {
  1513.     var em = Cc["@mozilla.org/extensions/manager;1"].
  1514.                getService(Ci.nsIExtensionManager);
  1515.     // Get the add-ons that are incompatible with the update's application
  1516.     // version and toolkit version.
  1517.     var currentAddons = em.getIncompatibleItemList("", this._update.extensionVersion,
  1518.                                                    this._update.platformVersion,
  1519.                                                    Ci.nsIUpdateItem.TYPE_ANY,
  1520.                                                    false, { });
  1521.     if (currentAddons.length > 0) {
  1522.       // Get the add-ons that are incompatible with the current application
  1523.       // version and toolkit version.
  1524.       var previousAddons = em.getIncompatibleItemList("", null, null,
  1525.                                                       Ci.nsIUpdateItem.TYPE_ANY,
  1526.                                                       false, { });
  1527.       // Don't include add-ons that are already incompatible with the current
  1528.       // application version and toolkit version.
  1529.       for (var i = 0; i < previousAddons.length; ++i) {
  1530.         for (var j = 0; j < currentAddons.length; ++j) {
  1531.           if (previousAddons[i].id === currentAddons[j].id) {
  1532.             currentAddons.splice(j, 1);
  1533.             break;
  1534.           }
  1535.         }
  1536.       }
  1537.     }
  1538.  
  1539.     if (currentAddons.length > 0) {
  1540.       /**
  1541. //@line 1572 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1542.        */
  1543.       this._incompatAddonsCount = currentAddons.length;
  1544.       LOG("UpdateService", "_checkAddonCompatibility - checking for " +
  1545.           "incompatible add-ons");
  1546.       var updateIncompatMode = getPref("getIntPref", PREF_APP_UPDATE_INCOMPATIBLE_MODE, 0);
  1547.       var mode = (updateIncompatMode == 1) ? Ci.nsIExtensionManager.UPDATE_CHECK_COMPATIBILITY :
  1548.                                              Ci.nsIExtensionManager.UPDATE_NOTIFY_NEWVERSION;
  1549.       em.update(currentAddons, currentAddons.length, mode, this,
  1550.                 this._update.extensionVersion, this._update.platformVersion);
  1551.     }
  1552.     else {
  1553.       LOG("UpdateService", "_checkAddonCompatibility - no need to show " +
  1554.           "prompt, just download the update");
  1555.       var status = this.downloadUpdate(this._update, true);
  1556.       if (status == STATE_NONE)
  1557.         cleanupActiveUpdate();
  1558.       this._update = null;
  1559.     }
  1560.   },
  1561.  
  1562.   /**
  1563.    * See nsIExtensionManager.idl
  1564.    */
  1565.   onUpdateStarted: function AUS_onUpdateStarted() {
  1566.   },
  1567.  
  1568.   /**
  1569.    * See nsIExtensionManager.idl
  1570.    */
  1571.   onUpdateEnded: function AUS_onUpdateEnded() {
  1572.     if (this._incompatAddonsCount > 0) {
  1573.       LOG("Checker", "onUpdateEnded - prompting because there are " +
  1574.           "incompatible add-ons");
  1575.       this._showPrompt(this._update);
  1576.     }
  1577.     else {
  1578.       LOG("UpdateService", "onUpdateEnded - no need to show prompt, just " +
  1579.           "download the update");
  1580.       var status = this.downloadUpdate(this._update, true);
  1581.       if (status == STATE_NONE)
  1582.         cleanupActiveUpdate();
  1583.     }
  1584.     this._update = null;
  1585.   },
  1586.  
  1587.   /**
  1588.    * See nsIExtensionManager.idl
  1589.    */
  1590.   onAddonUpdateStarted: function AUS_onAddonUpdateStarted(addon) {
  1591.   },
  1592.  
  1593.   /**
  1594.    * See nsIExtensionManager.idl
  1595.    */
  1596.   onAddonUpdateEnded: function AUS_onAddonUpdateEnded(addon, status) {
  1597.     if (status != Ci.nsIAddonUpdateCheckListener.STATUS_UPDATE &&
  1598.         status != Ci.nsIAddonUpdateCheckListener.STATUS_VERSIONINFO)
  1599.       return;
  1600.  
  1601.     LOG("UpdateService", "onAddonUpdateEnded - found update for add-on ID: " +
  1602.         addon.id);
  1603.     --this._incompatAddonsCount;
  1604.   },
  1605.  
  1606.   /**
  1607.    * The Checker used for background update checks.
  1608.    */
  1609.   _backgroundChecker: null,
  1610.  
  1611.   /**
  1612.    * See nsIUpdateService.idl
  1613.    */
  1614.   get backgroundChecker() {
  1615.     if (!this._backgroundChecker)
  1616.       this._backgroundChecker = new Checker();
  1617.     return this._backgroundChecker;
  1618.   },
  1619.  
  1620.   /**
  1621.    * See nsIUpdateService.idl
  1622.    */
  1623.   get canUpdate() {
  1624.     if (gCanUpdate !== null)
  1625.       return gCanUpdate;
  1626.  
  1627.     try {
  1628.       var appDirFile = getUpdateFile([FILE_PERMS_TEST]);
  1629.       LOG("UpdateService", "canUpdate - testing " + appDirFile.path);
  1630.       if (!appDirFile.exists()) {
  1631.         appDirFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  1632.         appDirFile.remove(false);
  1633.       }
  1634.       var updateDir = getUpdatesDir();
  1635.       var upDirFile = updateDir.clone();
  1636.       upDirFile.append(FILE_PERMS_TEST);
  1637.       LOG("UpdateService", "canUpdate - testing " + upDirFile.path);
  1638.       if (!upDirFile.exists()) {
  1639.         upDirFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  1640.         upDirFile.remove(false);
  1641.       }
  1642. //@line 1673 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1643.       var sysInfo = Cc["@mozilla.org/system-info;1"].
  1644.                     getService(Ci.nsIPropertyBag2);
  1645.  
  1646.       // Example windowsVersion:  Windows XP == 5.1
  1647.       var windowsVersion = sysInfo.getProperty("version");
  1648.       LOG("UpdateService", "canUpdate - windowsVersion = " + windowsVersion);
  1649.  
  1650.       // For Vista, updates can be performed to a location requiring 
  1651.       // admin privileges by requesting elevation via the UAC prompt when 
  1652.       // launching updater.exe if the appDir is under the Program Files 
  1653.       // directory (e.g. C:\Program Files\) and UAC is turned on and 
  1654.       // we can elevate (e.g. user has a split token)
  1655.       //
  1656.       // Note: this does note attempt to handle the case where UAC is
  1657.       // turned on and the installation directory is in a restricted
  1658.       // location that requires admin privileges to update other than 
  1659.       // Program Files.
  1660.  
  1661.       var userCanElevate = false;
  1662.  
  1663.       if (parseFloat(windowsVersion) >= 6) {
  1664.         try {
  1665.           var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
  1666.                             getService(Ci.nsIProperties);
  1667.           // KEY_UPDROOT will fail and throw an exception if
  1668.           // appDir is not under the Program Files, so we rely on that
  1669.           var dir = fileLocator.get(KEY_UPDROOT, Ci.nsIFile);
  1670.           // appDir is under Program Files, so check if the user can elevate
  1671.           userCanElevate = gApp.QueryInterface(Ci.nsIWinAppHelper).
  1672.                            userCanElevate;
  1673.           LOG("UpdateService", "canUpdate - on Vista, userCanElevate: " +
  1674.               userCanElevate);
  1675.         }
  1676.         catch (ex) {
  1677.           // When the installation directory is not under Program Files,
  1678.           // fall through to checking if write access to the 
  1679.           // installation directory is available.
  1680.           LOG("UpdateService", "canUpdate - on Vista, appDir is not under " +
  1681.               "Program Files");
  1682.         }
  1683.       }
  1684.  
  1685.       // On Windows, we no longer store the update under the app dir
  1686.       // if the app dir is under C:\Program Files.
  1687.       //
  1688.       // If we are on Windows (including Vista, if we can't elevate)
  1689.       // we need to check that
  1690.       // we can create and remove files from the actual app directory
  1691.       // (like C:\Program Files\Mozilla Firefox).  If we can't
  1692.       // (because this user is not an adminstrator, for example)
  1693.       // canUpdate() should return false.
  1694.       //
  1695.       // For Vista, we perform this check to enable updating the 
  1696.       // application when the user has write access to the installation 
  1697.       // directory under the following scenarios:
  1698.       // 1) the installation directory is not under Program Files 
  1699.       //    (e.g. C:\Program Files)
  1700.       // 2) UAC is turned off
  1701.       // 3) UAC is turned on and the user is not an admin 
  1702.       //    (e.g. the user does not have a split token)
  1703.       // 4) UAC is turned on and the user is already elevated,
  1704.       //    so they can't be elevated again.
  1705.       if (!userCanElevate) {
  1706.         // if we're unable to create the test file
  1707.         // the code below will throw an exception 
  1708.         var actualAppDir = getDir(KEY_APPDIR, []);
  1709.         var actualAppDirFile = actualAppDir.clone();
  1710.         actualAppDirFile.append(FILE_PERMS_TEST);
  1711.         LOG("UpdateService", "canUpdate - testing " + actualAppDirFile.path);
  1712.         if (!actualAppDirFile.exists()) {
  1713.           actualAppDirFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  1714.           actualAppDirFile.remove(false);
  1715.         }
  1716.       }
  1717. //@line 1748 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\update\src\nsUpdateService.js.in"
  1718.     }
  1719.     catch (e) {
  1720.        LOG("UpdateService", "canUpdate - unable to update. Exception: " + e);
  1721.       // No write privileges to install directory
  1722.       return gCanUpdate = false;
  1723.     }
  1724.     // If the administrator has locked the app update functionality
  1725.     // OFF - this is not just a user setting, so disable the manual
  1726.     // UI too.
  1727.     var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
  1728.     if (!enabled && gPref.prefIsLocked(PREF_APP_UPDATE_ENABLED)) {
  1729.       LOG("UpdateService", "canUpdate - unable to update, disabled by pref");
  1730.       return gCanUpdate = false;
  1731.     }
  1732.  
  1733.     // If we don't know the binary platform we're updating, we can't update.
  1734.     if (!gABI) {
  1735.       LOG("UpdateService", "canUpdate - unable tp update, unknown ABI");
  1736.       return gCanUpdate = false;
  1737.     }
  1738.  
  1739.     // If we don't know the OS version we're updating, we can't update.
  1740.     if (!gOSVersion) {
  1741.       LOG("UpdateService", "canUpdate unable to update, unknown OS version");
  1742.       return gCanUpdate = false;
  1743.     }
  1744.  
  1745.     LOG("UpdateService", "canUpdate - able to update");
  1746.     return gCanUpdate = true;
  1747.   },
  1748.  
  1749.   /**
  1750.    * See nsIUpdateService.idl
  1751.    */
  1752.   addDownloadListener: function AUS_addDownloadListener(listener) {
  1753.     if (!this._downloader) {
  1754.       LOG("UpdateService", "addDownloadListener - no downloader!");
  1755.       return;
  1756.     }
  1757.     this._downloader.addDownloadListener(listener);
  1758.   },
  1759.  
  1760.   /**
  1761.    * See nsIUpdateService.idl
  1762.    */
  1763.   removeDownloadListener: function AUS_removeDownloadListener(listener) {
  1764.     if (!this._downloader) {
  1765.       LOG("UpdateService", "removeDownloadListener - no downloader!");
  1766.       return;
  1767.     }
  1768.     this._downloader.removeDownloadListener(listener);
  1769.   },
  1770.  
  1771.   /**
  1772.    * See nsIUpdateService.idl
  1773.    */
  1774.   downloadUpdate: function AUS_downloadUpdate(update, background) {
  1775.     if (!update)
  1776.       throw Cr.NS_ERROR_NULL_POINTER;
  1777.  
  1778.     let ai = Cc["@mozilla.org/xre/app-info;1"].
  1779.              getService(Ci.nsIXULAppInfo);
  1780.     let vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
  1781.              getService(Ci.nsIVersionComparator);
  1782.     // Don't download the update if the update's version is less than the
  1783.     // current application's version.
  1784.     if (update.version && vc.compare(update.version, ai.version) < 0) {
  1785.       LOG("UpdateService", "downloadUpdate - removing update for previous " +
  1786.           "application version " + update.version);
  1787.       cleanupActiveUpdate();
  1788.       return STATE_NONE;
  1789.     }
  1790.  
  1791.     if (this.isDownloading) {
  1792.       if (update.isCompleteUpdate == this._downloader.isCompleteUpdate &&
  1793.           background == this._downloader.background) {
  1794.         LOG("UpdateService", "downloadUpdate - no support for downloading " +
  1795.             "more than one update at a time");
  1796.         return readStatusFile(getUpdatesDir());
  1797.       }
  1798.       this._downloader.cancel();
  1799.     }
  1800.     this._downloader = new Downloader(background);
  1801.     return this._downloader.downloadUpdate(update);
  1802.   },
  1803.  
  1804.   /**
  1805.    * See nsIUpdateService.idl
  1806.    */
  1807.   pauseDownload: function AUS_pauseDownload() {
  1808.     if (this.isDownloading)
  1809.       this._downloader.cancel();
  1810.   },
  1811.  
  1812.   /**
  1813.    * See nsIUpdateService.idl
  1814.    */
  1815.   get isDownloading() {
  1816.     return this._downloader && this._downloader.isBusy;
  1817.   },
  1818.  
  1819.   // nsIClassInfo
  1820.   flags: Ci.nsIClassInfo.SINGLETON,
  1821.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  1822.   getHelperForLanguage: function(language) null,
  1823.   getInterfaces: function AUS_getInterfaces(count) {
  1824.     var interfaces = [Ci.nsIApplicationUpdateService, Ci.nsITimerCallback,
  1825.                       Ci.nsIObserver];
  1826.     count.value = interfaces.length;
  1827.     return interfaces;
  1828.   },
  1829.  
  1830.   classDescription: "Update Service",
  1831.   contractID: "@mozilla.org/updates/update-service;1",
  1832.   classID: Components.ID("{B3C290A6-3943-4B89-8BBE-C01EB7B3B311}"),
  1833.   _xpcom_categories: [{ category: "app-startup", service: true }],
  1834.   _xpcom_factory: UpdateServiceFactory,
  1835.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIApplicationUpdateService,
  1836.                                          Ci.nsIAddonUpdateCheckListener,
  1837.                                          Ci.nsITimerCallback,
  1838.                                          Ci.nsIObserver])
  1839. };
  1840.  
  1841. /**
  1842.  * A service to manage active and past updates.
  1843.  * @constructor
  1844.  */
  1845. function UpdateManager() {
  1846.   // Ensure the Active Update file is loaded
  1847.   var updates = this._loadXMLFileIntoArray(getUpdateFile([FILE_UPDATE_ACTIVE]));
  1848.   if (updates.length > 0)
  1849.     this._activeUpdate = updates[0];
  1850. }
  1851. UpdateManager.prototype = {
  1852.   /**
  1853.    * All previously downloaded and installed updates, as an array of nsIUpdate
  1854.    * objects.
  1855.    */
  1856.   _updates: null,
  1857.  
  1858.   /**
  1859.    * The current actively downloading/installing update, as a nsIUpdate object.
  1860.    */
  1861.   _activeUpdate: null,
  1862.  
  1863.   /**
  1864.    * Loads an updates.xml formatted file into an array of nsIUpdate items.
  1865.    * @param   file
  1866.    *          A nsIFile for the updates.xml file
  1867.    * @returns The array of nsIUpdate items held in the file.
  1868.    */
  1869.   _loadXMLFileIntoArray: function UM__loadXMLFileIntoArray(file) {
  1870.     if (!file.exists()) {
  1871.       LOG("UpdateManager", "_loadXMLFileIntoArray: XML file does not exist");
  1872.       return [];
  1873.     }
  1874.  
  1875.     var result = [];
  1876.     var fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
  1877.                      createInstance(Ci.nsIFileInputStream);
  1878.     fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
  1879.     try {
  1880.       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
  1881.                    createInstance(Ci.nsIDOMParser);
  1882.       var doc = parser.parseFromStream(fileStream, "UTF-8",
  1883.                                        fileStream.available(), "text/xml");
  1884.  
  1885.       const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
  1886.       var updateCount = doc.documentElement.childNodes.length;
  1887.       for (var i = 0; i < updateCount; ++i) {
  1888.         var updateElement = doc.documentElement.childNodes.item(i);
  1889.         if (updateElement.nodeType != ELEMENT_NODE ||
  1890.             updateElement.localName != "update")
  1891.           continue;
  1892.  
  1893.         updateElement.QueryInterface(Ci.nsIDOMElement);
  1894.         try {
  1895.           var update = new Update(updateElement);
  1896.         } catch (e) {
  1897.           LOG("UpdateManager", "_loadXMLFileIntoArray - invalid update");
  1898.           continue;
  1899.         }
  1900.         result.push(update);
  1901.       }
  1902.     }
  1903.     catch (e) {
  1904.       LOG("UpdateManager", "_loadXMLFileIntoArray - error constructing " +
  1905.           "update list. Exception: " + e);
  1906.     }
  1907.     fileStream.close();
  1908.     return result;
  1909.   },
  1910.  
  1911.   /**
  1912.    * Load the update manager, initializing state from state files.
  1913.    */
  1914.   _ensureUpdates: function UM__ensureUpdates() {
  1915.     if (!this._updates) {
  1916.       this._updates = this._loadXMLFileIntoArray(getUpdateFile(
  1917.                         [FILE_UPDATES_DB]));
  1918.  
  1919.       var activeUpdates = this._loadXMLFileIntoArray(getUpdateFile(
  1920.                             [FILE_UPDATE_ACTIVE]));
  1921.       if (activeUpdates.length > 0)
  1922.         this._activeUpdate = activeUpdates[0];
  1923.     }
  1924.   },
  1925.  
  1926.   /**
  1927.    * See nsIUpdateService.idl
  1928.    */
  1929.   getUpdateAt: function UM_getUpdateAt(index) {
  1930.     this._ensureUpdates();
  1931.     return this._updates[index];
  1932.   },
  1933.  
  1934.   /**
  1935.    * See nsIUpdateService.idl
  1936.    */
  1937.   get updateCount() {
  1938.     this._ensureUpdates();
  1939.     return this._updates.length;
  1940.   },
  1941.  
  1942.   /**
  1943.    * See nsIUpdateService.idl
  1944.    */
  1945.   get activeUpdate() {
  1946.     if (this._activeUpdate &&
  1947.         this._activeUpdate.channel != getUpdateChannel()) {
  1948.       // User switched channels, clear out any old active updates and remove
  1949.       // partial downloads
  1950.       this._activeUpdate = null;
  1951.  
  1952.       // Destroy the updates directory, since we're done with it.
  1953.       cleanUpUpdatesDir();
  1954.     }
  1955.     return this._activeUpdate;
  1956.   },
  1957.   set activeUpdate(activeUpdate) {
  1958.     this._addUpdate(activeUpdate);
  1959.     this._activeUpdate = activeUpdate;
  1960.     if (!activeUpdate) {
  1961.       // If |activeUpdate| is null, we have updated both lists - the active list
  1962.       // and the history list, so we want to write both files.
  1963.       this.saveUpdates();
  1964.     }
  1965.     else
  1966.       this._writeUpdatesToXMLFile([this._activeUpdate],
  1967.                                   getUpdateFile([FILE_UPDATE_ACTIVE]));
  1968.     return activeUpdate;
  1969.   },
  1970.  
  1971.   /**
  1972.    * Add an update to the Updates list. If the item already exists in the list,
  1973.    * replace the existing value with the new value.
  1974.    * @param   update
  1975.    *          The nsIUpdate object to add.
  1976.    */
  1977.   _addUpdate: function UM__addUpdate(update) {
  1978.     if (!update)
  1979.       return;
  1980.     this._ensureUpdates();
  1981.     if (this._updates) {
  1982.       for (var i = 0; i < this._updates.length; ++i) {
  1983.         if (this._updates[i] &&
  1984.             this._updates[i].version == update.version &&
  1985.             this._updates[i].buildID == update.buildID) {
  1986.           // Replace the existing entry with the new value, updating
  1987.           // all metadata.
  1988.           this._updates[i] = update;
  1989.           return;
  1990.         }
  1991.       }
  1992.     }
  1993.     // Otherwise add it to the front of the list.
  1994.     this._updates.unshift(update);
  1995.   },
  1996.  
  1997.   /**
  1998.    * Serializes an array of updates to an XML file
  1999.    * @param   updates
  2000.    *          An array of nsIUpdate objects
  2001.    * @param   file
  2002.    *          The nsIFile object to serialize to
  2003.    */
  2004.   _writeUpdatesToXMLFile: function UM__writeUpdatesToXMLFile(updates, file) {
  2005.     var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"].
  2006.               createInstance(Ci.nsIFileOutputStream);
  2007.     var modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
  2008.     if (!file.exists())
  2009.       file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  2010.     fos.init(file, modeFlags, PERMS_FILE, 0);
  2011.  
  2012.     try {
  2013.       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
  2014.                    createInstance(Ci.nsIDOMParser);
  2015.       const EMPTY_UPDATES_DOCUMENT = "<?xml version=\"1.0\"?><updates xmlns=\"http://www.mozilla.org/2005/app-update\"></updates>";
  2016.       var doc = parser.parseFromString(EMPTY_UPDATES_DOCUMENT, "text/xml");
  2017.  
  2018.       for (var i = 0; i < updates.length; ++i) {
  2019.         if (updates[i])
  2020.           doc.documentElement.appendChild(updates[i].serialize(doc));
  2021.       }
  2022.  
  2023.       var serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
  2024.                        createInstance(Ci.nsIDOMSerializer);
  2025.       serializer.serializeToStream(doc.documentElement, fos, null);
  2026.     }
  2027.     catch (e) {
  2028.     }
  2029.  
  2030.     closeSafeOutputStream(fos);
  2031.   },
  2032.  
  2033.   /**
  2034.    * See nsIUpdateService.idl
  2035.    */
  2036.   saveUpdates: function UM_saveUpdates() {
  2037.     this._writeUpdatesToXMLFile([this._activeUpdate],
  2038.                                 getUpdateFile([FILE_UPDATE_ACTIVE]));
  2039.     if (this._activeUpdate)
  2040.       this._addUpdate(this._activeUpdate);
  2041.  
  2042.     // Don't write updates that have a temporary state to the updates.xml file.
  2043.     if (this._updates) {
  2044.       let updates = this._updates.slice();
  2045.       for (let i = updates.length - 1; i >= 0; --i) {
  2046.         let state = updates[i].state;
  2047.         if (state == STATE_NONE || state == STATE_DOWNLOADING ||
  2048.             state == STATE_PENDING) {
  2049.           updates.splice(i, 1);
  2050.         }
  2051.       }
  2052.  
  2053.       this._writeUpdatesToXMLFile(updates.slice(0, 10),
  2054.                                   getUpdateFile([FILE_UPDATES_DB]));
  2055.     }
  2056.   },
  2057.  
  2058.   classDescription: "Update Manager",
  2059.   contractID: "@mozilla.org/updates/update-manager;1",
  2060.   classID: Components.ID("{093C2356-4843-4C65-8709-D7DBCBBE7DFB}"),
  2061.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateManager])
  2062. };
  2063.  
  2064.  
  2065. /**
  2066.  * Checker
  2067.  * Checks for new Updates
  2068.  * @constructor
  2069.  */
  2070. function Checker() {
  2071. }
  2072. Checker.prototype = {
  2073.   /**
  2074.    * The XMLHttpRequest object that performs the connection.
  2075.    */
  2076.   _request  : null,
  2077.  
  2078.   /**
  2079.    * The nsIUpdateCheckListener callback
  2080.    */
  2081.   _callback : null,
  2082.  
  2083.   /**
  2084.    * The URL of the update service XML file to connect to that contains details
  2085.    * about available updates.
  2086.    */
  2087.   getUpdateURL: function UC_getUpdateURL(force) {
  2088.     this._forced = force;
  2089.  
  2090.     // Use the override URL if specified.
  2091.     var url = getPref("getCharPref", PREF_APP_UPDATE_URL_OVERRIDE, null);
  2092.  
  2093.     // Otherwise, construct the update URL from component parts.
  2094.     if (!url) {
  2095.       try {
  2096.         url = getDefaultPrefBranch().getCharPref(PREF_APP_UPDATE_URL);
  2097.       } catch (e) {
  2098.       }
  2099.     }
  2100.  
  2101.     if (!url || url == "") {
  2102.       LOG("Checker", "getUpdateURL - update URL not defined");
  2103.       return null;
  2104.     }
  2105.  
  2106.     url = url.replace(/%PRODUCT%/g, gApp.name);
  2107.     url = url.replace(/%VERSION%/g, gApp.version);
  2108.     url = url.replace(/%BUILD_ID%/g, gApp.appBuildID);
  2109.     url = url.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
  2110.     url = url.replace(/%OS_VERSION%/g, gOSVersion);
  2111.     if (/%LOCALE%/.test(url))
  2112.       url = url.replace(/%LOCALE%/g, getLocale());
  2113.     url = url.replace(/%CHANNEL%/g, getUpdateChannel());
  2114.     url = url.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
  2115.     url = url.replace(/%DISTRIBUTION%/g,
  2116.                       getDistributionPrefValue(PREF_APP_DISTRIBUTION));
  2117.     url = url.replace(/%DISTRIBUTION_VERSION%/g,
  2118.                       getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
  2119.     url = url.replace(/\+/g, "%2B");
  2120.  
  2121.     if (force)
  2122.       url += (url.indexOf("?") != -1 ? "&" : "?") + "force=1";
  2123.  
  2124.     LOG("Checker", "getUpdateURL - update URL: " + url);
  2125.     return url;
  2126.   },
  2127.  
  2128.   /**
  2129.    * See nsIUpdateService.idl
  2130.    */
  2131.   checkForUpdates: function UC_checkForUpdates(listener, force) {
  2132.     if (!listener)
  2133.       throw Cr.NS_ERROR_NULL_POINTER;
  2134.  
  2135.     var url = this.getUpdateURL(force);
  2136.     if (!url || (!this.enabled && !force))
  2137.       return;
  2138.  
  2139.     this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
  2140.                     createInstance(Ci.nsIXMLHttpRequest);
  2141.     this._request.open("GET", url, true);
  2142.     this._request.channel.notificationCallbacks = new BadCertHandler();
  2143.     this._request.overrideMimeType("text/xml");
  2144.     this._request.setRequestHeader("Cache-Control", "no-cache");
  2145.  
  2146.     var self = this;
  2147.     this._request.onerror     = function(event) { self.onError(event);    };
  2148.     this._request.onload      = function(event) { self.onLoad(event);     };
  2149.     this._request.onprogress  = function(event) { self.onProgress(event); };
  2150.  
  2151.     LOG("Checker", "checkForUpdates - sending request to: " + url);
  2152.     this._request.send(null);
  2153.  
  2154.     this._callback = listener;
  2155.   },
  2156.  
  2157.   /**
  2158.    * When progress associated with the XMLHttpRequest is received.
  2159.    * @param   event
  2160.    *          The nsIDOMLSProgressEvent for the load.
  2161.    */
  2162.   onProgress: function UC_onProgress(event) {
  2163.     LOG("Checker", "onProgress - " + event.position + "/" + event.totalSize);
  2164.     this._callback.onProgress(event.target, event.position, event.totalSize);
  2165.   },
  2166.  
  2167.   /**
  2168.    * Returns an array of nsIUpdate objects discovered by the update check.
  2169.    */
  2170.   get _updates() {
  2171.     var updatesElement = this._request.responseXML.documentElement;
  2172.     if (!updatesElement) {
  2173.       LOG("Checker", "get_updates - empty updates document?!");
  2174.       return [];
  2175.     }
  2176.  
  2177.     if (updatesElement.nodeName != "updates") {
  2178.       LOG("Checker", "get_updates - unexpected node name!");
  2179.       throw "";
  2180.     }
  2181.  
  2182.     const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
  2183.     var updates = [];
  2184.     for (var i = 0; i < updatesElement.childNodes.length; ++i) {
  2185.       var updateElement = updatesElement.childNodes.item(i);
  2186.       if (updateElement.nodeType != ELEMENT_NODE ||
  2187.           updateElement.localName != "update")
  2188.         continue;
  2189.  
  2190.       updateElement.QueryInterface(Ci.nsIDOMElement);
  2191.       try {
  2192.         var update = new Update(updateElement);
  2193.       } catch (e) {
  2194.         LOG("Checker", "get_updates - invalid <update/>, ignoring...");
  2195.         continue;
  2196.       }
  2197.       update.serviceURL = this.getUpdateURL(this._forced);
  2198.       update.channel = getUpdateChannel();
  2199.       updates.push(update);
  2200.     }
  2201.  
  2202.     return updates;
  2203.   },
  2204.  
  2205.   /**
  2206.    * Returns the status code for the XMLHttpRequest
  2207.    */
  2208.   _getChannelStatus: function UC__getChannelStatus(request) {
  2209.     var status = 0;
  2210.     try {
  2211.       status = request.status;
  2212.     }
  2213.     catch (e) {
  2214.     }
  2215.  
  2216.     if (status == 0)
  2217.       status = request.channel.QueryInterface(Ci.nsIRequest).status;
  2218.     return status;
  2219.   },
  2220.  
  2221.   /**
  2222.    * The XMLHttpRequest succeeded and the document was loaded.
  2223.    * @param   event
  2224.    *          The nsIDOMEvent for the load
  2225.    */
  2226.   onLoad: function UC_onLoad(event) {
  2227.     LOG("Checker", "onLoad - request completed downloading document");
  2228.  
  2229.     try {
  2230.       checkCert(this._request.channel);
  2231.       // Analyze the resulting DOM and determine the set of updates to install
  2232.       var updates = this._updates;
  2233.  
  2234.       LOG("Checker", "onLoad - number of updates available: " + updates.length);
  2235.  
  2236.       // ... and tell the Update Service about what we discovered.
  2237.       this._callback.onCheckComplete(event.target, updates, updates.length);
  2238.     }
  2239.     catch (e) {
  2240.       LOG("Checker", "onLoad - there was a problem with the update service " +
  2241.           "URL specified, either the XML file was malformed or it does not " +
  2242.           "exist at the location specified. Exception: " + e);
  2243.       var request = event.target;
  2244.       var status = this._getChannelStatus(request);
  2245.       LOG("Checker", "onLoad - request.status: " + status);
  2246.       var update = new Update(null);
  2247.       update.statusText = getStatusTextFromCode(status, 404);
  2248.       this._callback.onError(request, update);
  2249.     }
  2250.  
  2251.     this._request = null;
  2252.   },
  2253.  
  2254.   /**
  2255.    * There was an error of some kind during the XMLHttpRequest
  2256.    * @param   event
  2257.    *          The nsIDOMEvent for the load
  2258.    */
  2259.   onError: function UC_onError(event) {
  2260.     var request = event.target;
  2261.     var status = this._getChannelStatus(request);
  2262.     LOG("Checker", "onError - request.status: " + status);
  2263.  
  2264.     // If we can't find an error string specific to this status code,
  2265.     // just use the 200 message from above, which means everything
  2266.     // "looks" fine but there was probably an XML error or a bogus file.
  2267.     var update = new Update(null);
  2268.     update.statusText = getStatusTextFromCode(status, 200);
  2269.     this._callback.onError(request, update);
  2270.  
  2271.     this._request = null;
  2272.   },
  2273.  
  2274.   /**
  2275.    * Whether or not we are allowed to do update checking.
  2276.    */
  2277.   _enabled: true,
  2278.   get enabled() {
  2279.     var aus = Cc["@mozilla.org/updates/update-service;1"].
  2280.               getService(Ci.nsIApplicationUpdateService);
  2281.     var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true) &&
  2282.                   aus.canUpdate && this._enabled;
  2283.     return enabled;
  2284.   },
  2285.  
  2286.   /**
  2287.    * See nsIUpdateService.idl
  2288.    */
  2289.   stopChecking: function UC_stopChecking(duration) {
  2290.     // Always stop the current check
  2291.     if (this._request)
  2292.       this._request.abort();
  2293.  
  2294.     switch (duration) {
  2295.     case Ci.nsIUpdateChecker.CURRENT_SESSION:
  2296.       this._enabled = false;
  2297.       break;
  2298.     case Ci.nsIUpdateChecker.ANY_CHECKS:
  2299.       this._enabled = false;
  2300.       gPref.setBoolPref(PREF_APP_UPDATE_ENABLED, this._enabled);
  2301.       break;
  2302.     }
  2303.   },
  2304.  
  2305.   classDescription: "Update Checker",
  2306.   contractID: "@mozilla.org/updates/update-checker;1",
  2307.   classID: Components.ID("{898CDC9B-E43F-422F-9CC4-2F6291B415A3}"),
  2308.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateChecker])
  2309. };
  2310.  
  2311. /**
  2312.  * Manages the download of updates
  2313.  * @param   background
  2314.  *          Whether or not this downloader is operating in background
  2315.  *          update mode.
  2316.  * @constructor
  2317.  */
  2318. function Downloader(background) {
  2319.   this.background = background;
  2320. }
  2321. Downloader.prototype = {
  2322.   /**
  2323.    * The nsIUpdatePatch that we are downloading
  2324.    */
  2325.   _patch: null,
  2326.  
  2327.   /**
  2328.    * The nsIUpdate that we are downloading
  2329.    */
  2330.   _update: null,
  2331.  
  2332.   /**
  2333.    * The nsIIncrementalDownload object handling the download
  2334.    */
  2335.   _request: null,
  2336.  
  2337.   /**
  2338.    * Whether or not the update being downloaded is a complete replacement of
  2339.    * the user's existing installation or a patch representing the difference
  2340.    * between the new version and the previous version.
  2341.    */
  2342.   isCompleteUpdate: null,
  2343.  
  2344.   /**
  2345.    * Cancels the active download.
  2346.    */
  2347.   cancel: function Downloader_cancel() {
  2348.     if (this._request && this._request instanceof Ci.nsIRequest) {
  2349.       const NS_BINDING_ABORTED = 0x804b0002;
  2350.       this._request.cancel(NS_BINDING_ABORTED);
  2351.     }
  2352.   },
  2353.  
  2354.   /**
  2355.    * Whether or not a patch has been downloaded and staged for installation.
  2356.    */
  2357.   get patchIsStaged() {
  2358.     return readStatusFile(getUpdatesDir()) == STATE_PENDING;
  2359.   },
  2360.  
  2361.   /**
  2362.    * Verify the downloaded file.  We assume that the download is complete at
  2363.    * this point.
  2364.    */
  2365.   _verifyDownload: function Downloader__verifyDownload() {
  2366.     if (!this._request)
  2367.       return false;
  2368.  
  2369.     var destination = this._request.destination;
  2370.  
  2371.     // Ensure that the file size matches the expected file size.
  2372.     if (destination.fileSize != this._patch.size)
  2373.       return false;
  2374.  
  2375.     var fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
  2376.                      createInstance(Ci.nsIFileInputStream);
  2377.     fileStream.init(destination, MODE_RDONLY, PERMS_FILE, 0);
  2378.  
  2379.     try {
  2380.       var hash = Cc["@mozilla.org/security/hash;1"].
  2381.                  createInstance(Ci.nsICryptoHash);
  2382.       var hashFunction = Ci.nsICryptoHash[this._patch.hashFunction.toUpperCase()];
  2383.       if (hashFunction == undefined)
  2384.         throw Cr.NS_ERROR_UNEXPECTED;
  2385.       hash.init(hashFunction);
  2386.       hash.updateFromStream(fileStream, -1);
  2387.       // NOTE: For now, we assume that the format of _patch.hashValue is hex
  2388.       // encoded binary (such as what is typically output by programs like
  2389.       // sha1sum).  In the future, this may change to base64 depending on how
  2390.       // we choose to compute these hashes.
  2391.       digest = binaryToHex(hash.finish(false));
  2392.     } catch (e) {
  2393.       LOG("Downloader", "_verifyDownload - failed to compute hash of " +
  2394.           "downloaded update archive");
  2395.       digest = "";
  2396.     }
  2397.  
  2398.     fileStream.close();
  2399.  
  2400.     return digest == this._patch.hashValue.toLowerCase();
  2401.   },
  2402.  
  2403.   /**
  2404.    * Select the patch to use given the current state of updateDir and the given
  2405.    * set of update patches.
  2406.    * @param   update
  2407.    *          A nsIUpdate object to select a patch from
  2408.    * @param   updateDir
  2409.    *          A nsIFile representing the update directory
  2410.    * @returns A nsIUpdatePatch object to download
  2411.    */
  2412.   _selectPatch: function Downloader__selectPatch(update, updateDir) {
  2413.     // Given an update to download, we will always try to download the patch
  2414.     // for a partial update over the patch for a full update.
  2415.  
  2416.     /**
  2417.      * Return the first UpdatePatch with the given type.
  2418.      * @param   type
  2419.      *          The type of the patch ("complete" or "partial")
  2420.      * @returns A nsIUpdatePatch object matching the type specified
  2421.      */
  2422.     function getPatchOfType(type) {
  2423.       for (var i = 0; i < update.patchCount; ++i) {
  2424.         var patch = update.getPatchAt(i);
  2425.         if (patch && patch.type == type)
  2426.           return patch;
  2427.       }
  2428.       return null;
  2429.     }
  2430.  
  2431.     // Look to see if any of the patches in the Update object has been
  2432.     // pre-selected for download, otherwise we must figure out which one
  2433.     // to select ourselves.
  2434.     var selectedPatch = update.selectedPatch;
  2435.  
  2436.     var state = readStatusFile(updateDir);
  2437.  
  2438.     // If this is a patch that we know about, then select it.  If it is a patch
  2439.     // that we do not know about, then remove it and use our default logic.
  2440.     var useComplete = false;
  2441.     if (selectedPatch) {
  2442.       LOG("Downloader", "_selectPatch - found existing patch with state: " + state);
  2443.       switch (state) {
  2444.       case STATE_DOWNLOADING:
  2445.         LOG("Downloader", "_selectPatch - resuming download");
  2446.         return selectedPatch;
  2447.       case STATE_PENDING:
  2448.         LOG("Downloader", "_selectPatch - already downloaded and staged");
  2449.         return null;
  2450.       default:
  2451.         // Something went wrong when we tried to apply the previous patch.
  2452.         // Try the complete patch next time.
  2453.         if (update && selectedPatch.type == "partial") {
  2454.           useComplete = true;
  2455.         } else {
  2456.           // This is a pretty fatal error.  Just bail.
  2457.           LOG("Downloader", "_selectPatch - failed to apply complete patch!");
  2458.           writeStatusFile(updateDir, STATE_NONE);
  2459.           writeVersionFile(getUpdatesDir(), null);
  2460.           return null;
  2461.         }
  2462.       }
  2463.  
  2464.       selectedPatch = null;
  2465.     }
  2466.  
  2467.     // If we were not able to discover an update from a previous download, we
  2468.     // select the best patch from the given set.
  2469.     var partialPatch = getPatchOfType("partial");
  2470.     if (!useComplete)
  2471.       selectedPatch = partialPatch;
  2472.     if (!selectedPatch) {
  2473.       if (partialPatch)
  2474.         partialPatch.selected = false;
  2475.       selectedPatch = getPatchOfType("complete");
  2476.     }
  2477.  
  2478.     // Restore the updateDir since we may have deleted it.
  2479.     updateDir = getUpdatesDir();
  2480.  
  2481.     // if update only contains a partial patch, selectedPatch == null here if
  2482.     // the partial patch has been attempted and fails and we're trying to get a
  2483.     // complete patch
  2484.     if (selectedPatch)
  2485.       selectedPatch.selected = true;
  2486.  
  2487.     update.isCompleteUpdate = useComplete;
  2488.  
  2489.     // Reset the Active Update object on the Update Manager and flush the
  2490.     // Active Update DB.
  2491.     var um = Cc["@mozilla.org/updates/update-manager;1"].
  2492.              getService(Ci.nsIUpdateManager);
  2493.     um.activeUpdate = update;
  2494.  
  2495.     return selectedPatch;
  2496.   },
  2497.  
  2498.   /**
  2499.    * Whether or not we are currently downloading something.
  2500.    */
  2501.   get isBusy() {
  2502.     return this._request != null;
  2503.   },
  2504.  
  2505.   /**
  2506.    * Download and stage the given update.
  2507.    * @param   update
  2508.    *          A nsIUpdate object to download a patch for. Cannot be null.
  2509.    */
  2510.   downloadUpdate: function Downloader_downloadUpdate(update) {
  2511.     if (!update)
  2512.       throw Cr.NS_ERROR_NULL_POINTER;
  2513.  
  2514.     var updateDir = getUpdatesDir();
  2515.  
  2516.     this._update = update;
  2517.  
  2518.     // This function may return null, which indicates that there are no patches
  2519.     // to download.
  2520.     this._patch = this._selectPatch(update, updateDir);
  2521.     if (!this._patch) {
  2522.       LOG("Downloader", "downloadUpdate - no patch to download");
  2523.       return readStatusFile(updateDir);
  2524.     }
  2525.     this.isCompleteUpdate = this._patch.type == "complete";
  2526.  
  2527.     var patchFile = updateDir.clone();
  2528.     patchFile.append(FILE_UPDATE_ARCHIVE);
  2529.  
  2530.     var ios = Cc["@mozilla.org/network/io-service;1"].
  2531.               getService(Ci.nsIIOService);
  2532.     var uri = ios.newURI(this._patch.URL, null, null);
  2533.  
  2534.     this._request = Cc["@mozilla.org/network/incremental-download;1"].
  2535.                     createInstance(Ci.nsIIncrementalDownload);
  2536.  
  2537.     LOG("Downloader", "downloadUpdate - downloading from " + uri.spec + " to " +
  2538.         patchFile.path);
  2539.  
  2540.     var interval = this.background ? DOWNLOAD_BACKGROUND_INTERVAL
  2541.                                    : DOWNLOAD_FOREGROUND_INTERVAL;
  2542.     this._request.init(uri, patchFile, DOWNLOAD_CHUNK_SIZE, interval);
  2543.     this._request.start(this, null);
  2544.  
  2545.     writeStatusFile(updateDir, STATE_DOWNLOADING);
  2546.     this._patch.QueryInterface(Ci.nsIWritablePropertyBag);
  2547.     this._patch.state = STATE_DOWNLOADING;
  2548.     var um = Cc["@mozilla.org/updates/update-manager;1"].
  2549.              getService(Ci.nsIUpdateManager);
  2550.     um.saveUpdates();
  2551.     return STATE_DOWNLOADING;
  2552.   },
  2553.  
  2554.   /**
  2555.    * An array of download listeners to notify when we receive
  2556.    * nsIRequestObserver or nsIProgressEventSink method calls.
  2557.    */
  2558.   _listeners: [],
  2559.  
  2560.   /**
  2561.    * Adds a listener to the download process
  2562.    * @param   listener
  2563.    *          A download listener, implementing nsIRequestObserver and
  2564.    *          nsIProgressEventSink
  2565.    */
  2566.   addDownloadListener: function Downloader_addDownloadListener(listener) {
  2567.     for (var i = 0; i < this._listeners.length; ++i) {
  2568.       if (this._listeners[i] == listener)
  2569.         return;
  2570.     }
  2571.     this._listeners.push(listener);
  2572.   },
  2573.  
  2574.   /**
  2575.    * Removes a download listener
  2576.    * @param   listener
  2577.    *          The listener to remove.
  2578.    */
  2579.   removeDownloadListener: function Downloader_removeDownloadListener(listener) {
  2580.     for (var i = 0; i < this._listeners.length; ++i) {
  2581.       if (this._listeners[i] == listener) {
  2582.         this._listeners.splice(i, 1);
  2583.         return;
  2584.       }
  2585.     }
  2586.   },
  2587.  
  2588.   /**
  2589.    * When the async request begins
  2590.    * @param   request
  2591.    *          The nsIRequest object for the transfer
  2592.    * @param   context
  2593.    *          Additional data
  2594.    */
  2595.   onStartRequest: function Downloader_onStartRequest(request, context) {
  2596.     if (request instanceof Ci.nsIIncrementalDownload)
  2597.       LOG("Downloader", "onStartRequest - spec: " + request.URI.spec);
  2598.  
  2599.     var listenerCount = this._listeners.length;
  2600.     for (var i = 0; i < listenerCount; ++i)
  2601.       this._listeners[i].onStartRequest(request, context);
  2602.   },
  2603.  
  2604.   /**
  2605.    * When new data has been downloaded
  2606.    * @param   request
  2607.    *          The nsIRequest object for the transfer
  2608.    * @param   context
  2609.    *          Additional data
  2610.    * @param   progress
  2611.    *          The current number of bytes transferred
  2612.    * @param   maxProgress
  2613.    *          The total number of bytes that must be transferred
  2614.    */
  2615.   onProgress: function Downloader_onProgress(request, context, progress,
  2616.                                              maxProgress) {
  2617.     LOG("Downloader.onProgress", "onProgress - progress: " + progress + "/" +
  2618.         maxProgress);
  2619.  
  2620.     var listenerCount = this._listeners.length;
  2621.     for (var i = 0; i < listenerCount; ++i) {
  2622.       var listener = this._listeners[i];
  2623.       if (listener instanceof Ci.nsIProgressEventSink)
  2624.         listener.onProgress(request, context, progress, maxProgress);
  2625.     }
  2626.   },
  2627.  
  2628.   /**
  2629.    * When we have new status text
  2630.    * @param   request
  2631.    *          The nsIRequest object for the transfer
  2632.    * @param   context
  2633.    *          Additional data
  2634.    * @param   status
  2635.    *          A status code
  2636.    * @param   statusText
  2637.    *          Human readable version of |status|
  2638.    */
  2639.   onStatus: function Downloader_onStatus(request, context, status, statusText) {
  2640.     LOG("Downloader", "onStatus - status: " + status + ", statusText: " +
  2641.         statusText);
  2642.  
  2643.     var listenerCount = this._listeners.length;
  2644.     for (var i = 0; i < listenerCount; ++i) {
  2645.       var listener = this._listeners[i];
  2646.       if (listener instanceof Ci.nsIProgressEventSink)
  2647.         listener.onStatus(request, context, status, statusText);
  2648.     }
  2649.   },
  2650.  
  2651.   /**
  2652.    * When data transfer ceases
  2653.    * @param   request
  2654.    *          The nsIRequest object for the transfer
  2655.    * @param   context
  2656.    *          Additional data
  2657.    * @param   status
  2658.    *          Status code containing the reason for the cessation.
  2659.    */
  2660.   onStopRequest: function  Downloader_onStopRequest(request, context, status) {
  2661.     if (request instanceof Ci.nsIIncrementalDownload)
  2662.       LOG("Downloader", "onStopRequest - spec: " + request.URI.spec +
  2663.           ", status: " + status);
  2664.  
  2665.     var state = this._patch.state;
  2666.     var shouldShowPrompt = false;
  2667.     var deleteActiveUpdate = false;
  2668.     const NS_BINDING_ABORTED = 0x804b0002;
  2669.     const NS_ERROR_ABORT = 0x80004004;
  2670.     if (Components.isSuccessCode(status)) {
  2671.       var sbs = Cc["@mozilla.org/intl/stringbundle;1"].
  2672.                 getService(Ci.nsIStringBundleService);
  2673.       var updateStrings = sbs.createBundle(URI_UPDATES_PROPERTIES);
  2674.       if (this._verifyDownload()) {
  2675.         state = STATE_PENDING;
  2676.  
  2677.         // We only need to explicitly show the prompt if this is a backround
  2678.         // download, since otherwise some kind of UI is already visible and
  2679.         // that UI will notify.
  2680.         if (this.background)
  2681.           shouldShowPrompt = true;
  2682.  
  2683.         // Tell the updater.exe we're ready to apply.
  2684.         writeStatusFile(getUpdatesDir(), state);
  2685.         writeVersionFile(getUpdatesDir(), this._update.extensionVersion);
  2686.         this._update.installDate = (new Date()).getTime();
  2687.         this._update.statusText = updateStrings.GetStringFromName("installPending");
  2688.       }
  2689.       else {
  2690.         LOG("Downloader", "onStopRequest - download verification failed");
  2691.         state = STATE_DOWNLOAD_FAILED;
  2692.  
  2693.         // TODO: use more informative error code here
  2694.         status = Cr.NS_ERROR_UNEXPECTED;
  2695.  
  2696.         // Yes, this code is a string.
  2697.         const vfCode = "verification_failed";
  2698.         var message = getStatusTextFromCode(vfCode, vfCode);
  2699.         this._update.statusText = message;
  2700.  
  2701.         if (this._update.isCompleteUpdate)
  2702.           deleteActiveUpdate = true;
  2703.  
  2704.         // Destroy the updates directory, since we're done with it.
  2705.         cleanUpUpdatesDir();
  2706.       }
  2707.     }
  2708.     else if (status != NS_BINDING_ABORTED &&
  2709.              status != NS_ERROR_ABORT) {
  2710.       LOG("Downloader", "onStopRequest - non-verification failure");
  2711.       // Some sort of other failure, log this in the |statusText| property
  2712.       state = STATE_DOWNLOAD_FAILED;
  2713.  
  2714.       // XXXben - if |request| (The Incremental Download) provided a means
  2715.       // for accessing the http channel we could do more here.
  2716.  
  2717.       const NS_BINDING_FAILED = 2152398849;
  2718.       this._update.statusText = getStatusTextFromCode(status,
  2719.         NS_BINDING_FAILED);
  2720.  
  2721.       // Destroy the updates directory, since we're done with it.
  2722.       cleanUpUpdatesDir();
  2723.  
  2724.       deleteActiveUpdate = true;
  2725.     }
  2726.     LOG("Downloader", "onStopRequest - setting state to: " + state);
  2727.     this._patch.state = state;
  2728.     var um = Cc["@mozilla.org/updates/update-manager;1"].
  2729.              getService(Ci.nsIUpdateManager);
  2730.     if (deleteActiveUpdate) {
  2731.       this._update.installDate = (new Date()).getTime();
  2732.       um.activeUpdate = null;
  2733.     }
  2734.     else {
  2735.       um.activeUpdate.state = state;
  2736.     }
  2737.     um.saveUpdates();
  2738.  
  2739.     var listenerCount = this._listeners.length;
  2740.     for (var i = 0; i < listenerCount; ++i)
  2741.       this._listeners[i].onStopRequest(request, context, status);
  2742.  
  2743.     this._request = null;
  2744.  
  2745.     if (state == STATE_DOWNLOAD_FAILED) {
  2746.       if (!this._update.isCompleteUpdate) {
  2747.         var allFailed = true;
  2748.  
  2749.         // If we were downloading a patch and the patch verification phase
  2750.         // failed, log this and then commence downloading the complete update.
  2751.         LOG("Downloader", "onStopRequest - verification of patch failed, " +
  2752.             "downloading complete update");
  2753.         this._update.isCompleteUpdate = true;
  2754.         var status = this.downloadUpdate(this._update);
  2755.  
  2756.         if (status == STATE_NONE) {
  2757.           cleanupActiveUpdate();
  2758.         } else {
  2759.           allFailed = false;
  2760.         }
  2761.         // This will reset the |.state| property on this._update if a new
  2762.         // download initiates.
  2763.       }
  2764.  
  2765.       // if we still fail after trying a complete download, give up completely
  2766.       if (allFailed) {
  2767.         // In all other failure cases, i.e. we're S.O.L. - no more failing over
  2768.         // ...
  2769.  
  2770.         // If this was ever a foreground download, and now there is no UI active
  2771.         // (e.g. because the user closed the download window) and there was an
  2772.         // error, we must notify now. Otherwise we can keep the failure to
  2773.         // ourselves since the user won't be expecting it.
  2774.         try {
  2775.           this._update.QueryInterface(Ci.nsIWritablePropertyBag);
  2776.           var fgdl = this._update.getProperty("foregroundDownload");
  2777.         }
  2778.         catch (e) {
  2779.         }
  2780.  
  2781.         if (fgdl == "true") {
  2782.           var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
  2783.                          createInstance(Ci.nsIUpdatePrompt);
  2784.           this._update.QueryInterface(Ci.nsIWritablePropertyBag);
  2785.           this._update.setProperty("downloadFailed", "true");
  2786.           prompter.showUpdateError(this._update);
  2787.         }
  2788.       }
  2789.  
  2790.       // Prevent leaking the update object (bug 454964)
  2791.       this._update = null;
  2792.       // the complete download succeeded or total failure was handled, so exit
  2793.       return;
  2794.     }
  2795.  
  2796.     // Do this after *everything* else, since it will likely cause the app
  2797.     // to shut down.
  2798.     if (shouldShowPrompt) {
  2799.       // Notify the user that an update has been downloaded and is ready for
  2800.       // installation (i.e. that they should restart the application). We do
  2801.       // not notify on failed update attempts.
  2802.       var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
  2803.                      createInstance(Ci.nsIUpdatePrompt);
  2804.       prompter.showUpdateDownloaded(this._update, true);
  2805.     }
  2806.     // Prevent leaking the update object (bug 454964)
  2807.     this._update = null;
  2808.   },
  2809.  
  2810.   /**
  2811.    * See nsIInterfaceRequestor.idl
  2812.    */
  2813.   getInterface: function Downloader_getInterface(iid) {
  2814.     // The network request may require proxy authentication, so provide the
  2815.     // default nsIAuthPrompt if requested.
  2816.     if (iid.equals(Ci.nsIAuthPrompt)) {
  2817.       var prompt = Cc["@mozilla.org/network/default-auth-prompt;1"].
  2818.                    createInstance();
  2819.       return prompt.QueryInterface(iid);
  2820.     }
  2821.     throw Components.results.NS_NOINTERFACE;
  2822.   },
  2823.  
  2824.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
  2825.                                          Ci.nsIProgressEventSink,
  2826.                                          Ci.nsIInterfaceRequestor])
  2827. };
  2828.  
  2829. /**
  2830.  * A manager for update check timers. Manages timers that fire over long
  2831.  * periods of time (e.g. days, weeks).
  2832.  * @constructor
  2833.  */
  2834. function TimerManager() {
  2835.   getObserverService().addObserver(this, "xpcom-shutdown", false);
  2836.  
  2837.   this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  2838.   var timerInterval = getPref("getIntPref", PREF_APP_UPDATE_TIMER, 600000);
  2839.   this._timer.initWithCallback(this, timerInterval,
  2840.                                Ci.nsITimer.TYPE_REPEATING_SLACK);
  2841. }
  2842. TimerManager.prototype = {
  2843.   /**
  2844.    * See nsIObserver.idl
  2845.    */
  2846.   observe: function TM_observe(subject, topic, data) {
  2847.     if (topic == "xpcom-shutdown") {
  2848.      getObserverService().removeObserver(this, "xpcom-shutdown");
  2849.  
  2850.       // Release everything we hold onto.
  2851.       for (var timerID in this._timers)
  2852.         delete this._timers[timerID];
  2853.       this._timer = null;
  2854.       this._timers = null;
  2855.     }
  2856.   },
  2857.  
  2858.   /**
  2859.    * The Checker Timer
  2860.    */
  2861.   _timer: null,
  2862.  
  2863.   /**
  2864.    * The set of registered timers.
  2865.    */
  2866.   _timers: { },
  2867.  
  2868.   /**
  2869.    * Called when the checking timer fires.
  2870.    * @param   timer
  2871.    *          The checking timer that fired.
  2872.    */
  2873.   notify: function TM_notify(timer) {
  2874.     for (var timerID in this._timers) {
  2875.       var timerData = this._timers[timerID];
  2876.       var lastUpdateTime = timerData.lastUpdateTime;
  2877.       var now = Math.round(Date.now() / 1000);
  2878.  
  2879.       // Fudge the lastUpdateTime by some random increment of the update
  2880.       // check interval (e.g. some random slice of 10 minutes) so that when
  2881.       // the time comes to check, we offset each client request by a random
  2882.       // amount so they don't all hit at once. app.update.timer is in milliseconds,
  2883.       // whereas app.update.lastUpdateTime is in seconds
  2884.       var timerInterval = getPref("getIntPref", PREF_APP_UPDATE_TIMER, 600000);
  2885.       lastUpdateTime += Math.round(Math.random() * timerInterval / 1000);
  2886.  
  2887.       if ((now - lastUpdateTime) > timerData.interval &&
  2888.           timerData.callback instanceof Ci.nsITimerCallback) {
  2889.         timerData.callback.notify(timer);
  2890.         timerData.lastUpdateTime = now;
  2891.         var preference = PREF_APP_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, timerID);
  2892.         gPref.setIntPref(preference, now);
  2893.       }
  2894.     }
  2895.   },
  2896.  
  2897.   /**
  2898.    * See nsIUpdateService.idl
  2899.    */
  2900.   registerTimer: function TM_registerTimer(id, callback, interval) {
  2901.     var preference = PREF_APP_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, id);
  2902.     var now = Math.round(Date.now() / 1000);
  2903.     var lastUpdateTime = null;
  2904.     if (gPref.prefHasUserValue(preference)) {
  2905.       lastUpdateTime = gPref.getIntPref(preference);
  2906.     } else {
  2907.       gPref.setIntPref(preference, now);
  2908.       lastUpdateTime = now;
  2909.     }
  2910.     this._timers[id] = { callback       : callback,
  2911.                          interval       : interval,
  2912.                          lastUpdateTime : lastUpdateTime };
  2913.   },
  2914.  
  2915.   classDescription: "Timer Manager",
  2916.   contractID: "@mozilla.org/updates/timer-manager;1",
  2917.   classID: Components.ID("{B322A5C0-A419-484E-96BA-D7182163899F}"),
  2918.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateTimerManager,
  2919.                                          Ci.nsITimerCallback,
  2920.                                          Ci.nsIObserver])
  2921. };
  2922.  
  2923. /**
  2924.  * UpdatePrompt
  2925.  * An object which can prompt the user with information about updates, request
  2926.  * action, etc. Embedding clients can override this component with one that
  2927.  * invokes a native front end.
  2928.  * @constructor
  2929.  */
  2930. function UpdatePrompt() {
  2931. }
  2932. UpdatePrompt.prototype = {
  2933.   /**
  2934.    * See nsIUpdateService.idl
  2935.    */
  2936.   checkForUpdates: function UP_checkForUpdates() {
  2937.     this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, UPDATE_WINDOW_NAME,
  2938.                  null, null);
  2939.   },
  2940.  
  2941.   /**
  2942.    * See nsIUpdateService.idl
  2943.    */
  2944.   showUpdateAvailable: function UP_showUpdateAvailable(update) {
  2945.     if (!this._enabled || this._getUpdateWindow())
  2946.       return;
  2947.  
  2948.     var bundle = this._updateBundle;
  2949.     var stringsPrefix = "updateAvailable_" + update.type + ".";
  2950.     var title = bundle.formatStringFromName(stringsPrefix + "title",
  2951.                                             [update.name], 1);
  2952.     var text = bundle.GetStringFromName(stringsPrefix + "text");
  2953.     var imageUrl = "";
  2954.     this._showUnobtrusiveUI(null, URI_UPDATE_PROMPT_DIALOG, null,
  2955.                            UPDATE_WINDOW_NAME, "updatesavailable", update,
  2956.                            title, text, imageUrl);
  2957.   },
  2958.  
  2959.   /**
  2960.    * See nsIUpdateService.idl
  2961.    */
  2962.   showUpdateDownloaded: function UP_showUpdateDownloaded(update, background) {
  2963.     if (background) {
  2964.       if (!this._enabled)
  2965.         return;
  2966.  
  2967.       var bundle = this._updateBundle;
  2968.       var stringsPrefix = "updateDownloaded_" + update.type + ".";
  2969.       var title = bundle.formatStringFromName(stringsPrefix + "title",
  2970.                                               [update.name], 1);
  2971.       var text = bundle.GetStringFromName(stringsPrefix + "text");
  2972.       var imageUrl = "";
  2973.       this._showUnobtrusiveUI(null, URI_UPDATE_PROMPT_DIALOG, null,
  2974.                               UPDATE_WINDOW_NAME, "finishedBackground", update,
  2975.                               title, text, imageUrl);
  2976.     } else {
  2977.       this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null,
  2978.                    UPDATE_WINDOW_NAME, "finishedBackground", update);
  2979.     }
  2980.   },
  2981.  
  2982.   /**
  2983.    * See nsIUpdateService.idl
  2984.    */
  2985.   showUpdateInstalled: function UP_showUpdateInstalled() {
  2986.     if (!this._enabled || this._getUpdateWindow() ||
  2987.         !getPref("getBoolPref", PREF_APP_UPDATE_SHOW_INSTALLED_UI, true))
  2988.       return;
  2989.  
  2990.     var page = "installed";
  2991.     var win = this._getUpdateWindow();
  2992.     if (win) {
  2993.       if (page && "setCurrentPage" in win)
  2994.         win.setCurrentPage(page);
  2995.       win.focus();
  2996.     }
  2997.     else {
  2998.       var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
  2999.       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
  3000.              getService(Ci.nsIWindowWatcher);
  3001.       var arg = Cc["@mozilla.org/supports-string;1"].
  3002.                 createInstance(Ci.nsISupportsString);
  3003.       arg.data = page;
  3004.       ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, null, openFeatures, arg);
  3005.     }
  3006.   },
  3007.  
  3008.   /**
  3009.    * See nsIUpdateService.idl
  3010.    */
  3011.   showUpdateError: function UP_showUpdateError(update) {
  3012.     if (!this._enabled)
  3013.       return;
  3014.  
  3015.     // In some cases, we want to just show a simple alert dialog:
  3016.     if (update.state == STATE_FAILED && update.errorCode == WRITE_ERROR) {
  3017.       var updateBundle = this._updateBundle;
  3018.       var title = updateBundle.GetStringFromName("updaterIOErrorTitle");
  3019.       var text = updateBundle.formatStringFromName("updaterIOErrorMsg",
  3020.                                                    [gApp.name, gApp.name], 2);
  3021.       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
  3022.                getService(Ci.nsIWindowWatcher);
  3023.       ww.getNewPrompter(null).alert(title, text);
  3024.     } else {
  3025.       this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, UPDATE_WINDOW_NAME,
  3026.                    "errors", update);
  3027.     }
  3028.   },
  3029.  
  3030.   /**
  3031.    * See nsIUpdateService.idl
  3032.    */
  3033.   showUpdateHistory: function UP_showUpdateHistory(parent) {
  3034.     this._showUI(parent, URI_UPDATE_HISTORY_DIALOG, "modal,dialog=yes",
  3035.                  "Update:History", null, null);
  3036.   },
  3037.  
  3038.   /**
  3039.    * Whether or not we are enabled (i.e. not in Silent mode)
  3040.    */
  3041.   get _enabled() {
  3042.     return !getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false);
  3043.   },
  3044.  
  3045.   get _updateBundle() {
  3046.     return Cc["@mozilla.org/intl/stringbundle;1"].
  3047.            getService(Ci.nsIStringBundleService).
  3048.            createBundle(URI_UPDATES_PROPERTIES);
  3049.   },
  3050.  
  3051.   /**
  3052.    * Returns the update window if present.
  3053.    */
  3054.   _getUpdateWindow: function UP__getUpdateWindow() {
  3055.     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
  3056.              getService(Ci.nsIWindowMediator);
  3057.     return wm.getMostRecentWindow(UPDATE_WINDOW_NAME);
  3058.   },
  3059.  
  3060.   /**
  3061.    * Initiate a less obtrusive UI, starting with a non-modal notification alert
  3062.    * @param   parent
  3063.    *          A parent window, can be null
  3064.    * @param   uri
  3065.    *          The URI string of the dialog to show
  3066.    * @param   name
  3067.    *          The Window Name of the dialog to show, in case it is already open
  3068.    *          and can merely be focused
  3069.    * @param   page
  3070.    *          The page of the wizard to be displayed, if one is already open.
  3071.    * @param   update
  3072.    *          An update to pass to the UI in the window arguments.
  3073.    *          Can be null
  3074.    * @param   title
  3075.    *          The title for the notification alert.
  3076.    * @param   text
  3077.    *          The contents of the notification alert.
  3078.    * @param   imageUrl
  3079.    *          A URL identifying the image to put in the notification alert.
  3080.    */
  3081.   _showUnobtrusiveUI: function UP__showUnobUI(parent, uri, features, name, page,
  3082.                                               update, title, text, imageUrl) {
  3083.     var observer = {
  3084.       updatePrompt: this,
  3085.       service: null,
  3086.       timer: null,
  3087.       notify: function () {
  3088.         // the user hasn't restarted yet => prompt when idle
  3089.         this.service.removeObserver(this, "quit-application");
  3090.         // If the update window is already open skip showing the UI
  3091.         if (this.updatePrompt._getUpdateWindow())
  3092.           return;
  3093.         this.updatePrompt._showUIWhenIdle(parent, uri, features, name, page, update);
  3094.       },
  3095.       observe: function (aSubject, aTopic, aData) {
  3096.         switch (aTopic) {
  3097.           case "alertclickcallback":
  3098.             this.updatePrompt._showUI(parent, uri, features, name, page, update);
  3099.             // fall thru
  3100.           case "quit-application":
  3101.             this.timer.cancel();
  3102.             this.service.removeObserver(this, "quit-application");
  3103.             break;
  3104.         }
  3105.       }
  3106.     };
  3107.  
  3108.     try {
  3109.       var notifier = Cc["@mozilla.org/alerts-service;1"].
  3110.                      getService(Ci.nsIAlertsService);
  3111.       notifier.showAlertNotification(imageUrl, title, text, true, "", observer);
  3112.     }
  3113.     catch (e) {
  3114.       // Failed to retrieve alerts service, platform unsupported
  3115.       this._showUIWhenIdle(parent, uri, features, name, page, update);
  3116.       return;
  3117.     }
  3118.  
  3119.     observer.service = Cc["@mozilla.org/observer-service;1"].
  3120.                        getService(Ci.nsIObserverService);
  3121.     observer.service.addObserver(observer, "quit-application", false);
  3122.  
  3123.     // Give the user x seconds to react before showing the big UI
  3124.     var promptWaitTime = getPref("getIntPref", PREF_APP_UPDATE_PROMPTWAITTIME, 43200);
  3125.     observer.timer = Cc["@mozilla.org/timer;1"].
  3126.                      createInstance(Ci.nsITimer);
  3127.     observer.timer.initWithCallback(observer, promptWaitTime * 1000,
  3128.                                     observer.timer.TYPE_ONE_SHOT);
  3129.   },
  3130.  
  3131.   /**
  3132.    * Show the UI when the user was idle
  3133.    * @param   parent
  3134.    *          A parent window, can be null
  3135.    * @param   uri
  3136.    *          The URI string of the dialog to show
  3137.    * @param   name
  3138.    *          The Window Name of the dialog to show, in case it is already open
  3139.    *          and can merely be focused
  3140.    * @param   page
  3141.    *          The page of the wizard to be displayed, if one is already open.
  3142.    * @param   update
  3143.    *          An update to pass to the UI in the window arguments.
  3144.    *          Can be null
  3145.    */
  3146.   _showUIWhenIdle: function UP__showUIWhenIdle(parent, uri, features, name,
  3147.                                                page, update) {
  3148.     var idleService = Cc["@mozilla.org/widget/idleservice;1"].
  3149.                       getService(Ci.nsIIdleService);
  3150.  
  3151.     const IDLE_TIME = getPref("getIntPref", PREF_APP_UPDATE_IDLETIME, 60);
  3152.     if (idleService.idleTime / 1000 >= IDLE_TIME) {
  3153.       this._showUI(parent, uri, features, name, page, update);
  3154.     } else {
  3155.       var observerService = Cc["@mozilla.org/observer-service;1"].
  3156.                             getService(Ci.nsIObserverService);
  3157.       var observer = {
  3158.         updatePrompt: this,
  3159.         observe: function (aSubject, aTopic, aData) {
  3160.           switch (aTopic) {
  3161.             case "idle":
  3162.               // If the update window is already open skip showing the UI
  3163.               if (!this.updatePrompt._getUpdateWindow())
  3164.                 this.updatePrompt._showUI(parent, uri, features, name, page, update);
  3165.               // fall thru
  3166.             case "quit-application":
  3167.               idleService.removeIdleObserver(this, IDLE_TIME);
  3168.               observerService.removeObserver(this, "quit-application");
  3169.               break;
  3170.           }
  3171.         }
  3172.       };
  3173.       idleService.addIdleObserver(observer, IDLE_TIME);
  3174.       observerService.addObserver(observer, "quit-application", false);
  3175.     }
  3176.   },
  3177.  
  3178.   /**
  3179.    * Show the Update Checking UI
  3180.    * @param   parent
  3181.    *          A parent window, can be null
  3182.    * @param   uri
  3183.    *          The URI string of the dialog to show
  3184.    * @param   name
  3185.    *          The Window Name of the dialog to show, in case it is already open
  3186.    *          and can merely be focused
  3187.    * @param   page
  3188.    *          The page of the wizard to be displayed, if one is already open.
  3189.    * @param   update
  3190.    *          An update to pass to the UI in the window arguments.
  3191.    *          Can be null
  3192.    */
  3193.   _showUI: function UP__showUI(parent, uri, features, name, page, update) {
  3194.     var ary = null;
  3195.     if (update) {
  3196.       ary = Cc["@mozilla.org/supports-array;1"].
  3197.             createInstance(Ci.nsISupportsArray);
  3198.       ary.AppendElement(update);
  3199.     }
  3200.  
  3201.     var win = this._getUpdateWindow();
  3202.     if (win) {
  3203.       if (page && "setCurrentPage" in win)
  3204.         win.setCurrentPage(page);
  3205.       win.focus();
  3206.     }
  3207.     else {
  3208.       var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
  3209.       if (features)
  3210.         openFeatures += "," + features;
  3211.       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
  3212.                getService(Ci.nsIWindowWatcher);
  3213.       ww.openWindow(parent, uri, "", openFeatures, ary);
  3214.     }
  3215.   },
  3216.  
  3217.   classDescription: "Update Prompt",
  3218.   contractID: "@mozilla.org/updates/update-prompt;1",
  3219.   classID: Components.ID("{27ABA825-35B5-4018-9FDD-F99250A0E722}"),
  3220.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt])
  3221. };
  3222.  
  3223. function NSGetModule(compMgr, fileSpec)
  3224.   XPCOMUtils.generateModule([UpdateService, Checker, UpdatePrompt, TimerManager, UpdateManager]);
  3225.  
  3226.